From 7321bd7464a5985ea71679736d0a5c9c9a8e3bcc Mon Sep 17 00:00:00 2001 From: Vineeth TM Date: Thu, 9 Jul 2015 11:24:31 +0900 Subject: [PATCH] add GstValidate package Change-Id: I4fa1b012bd1ed38896ede255478219af7e5bec58 Signed-off-by: Vineeth TM --- gst-integration-testsuites | 1 + packaging/gst-validate.spec | 95 + packaging/gst-validate.spec~ | 96 + validate/.gitignore | 51 + validate/.gitmodules | 0 validate/AUTHORS | 0 validate/COPYING | 504 + validate/ChangeLog | 10872 +++++++++++++++++++ validate/Makefile.am | 79 + validate/NEWS | 1 + validate/README | 48 + validate/autogen.sh | 109 + validate/common/ChangeLog | 1712 +++ validate/common/MAINTAINERS | 12 + validate/common/Makefile.am | 22 + validate/common/README | 255 + validate/common/README.static-linking | 174 + validate/common/autogen.sh.in | 98 + validate/common/c-to-xml.py | 36 + validate/common/check-exports | 54 + validate/common/check.mak | 251 + validate/common/coverage/coverage-report-entry.pl | 69 + validate/common/coverage/coverage-report.pl | 125 + validate/common/coverage/coverage-report.xsl | 235 + validate/common/coverage/lcov.mak | 47 + validate/common/cruft.mak | 56 + validate/common/download-translations | 152 + .../common/extract-release-date-from-doap-file | 32 + validate/common/gen-changelog.py | 99 + validate/common/gettext.patch | 12 + validate/common/glib-gen.mak | 44 + validate/common/gst-autogen.sh | 297 + validate/common/gst-glib-gen.mak | 45 + validate/common/gst-indent | 55 + validate/common/gst.supp | 4016 +++++++ validate/common/gstdoc-scangobj | 1617 +++ validate/common/gtk-doc-plugins.mak | 382 + validate/common/gtk-doc.mak | 227 + validate/common/hooks/install-hooks | 97 + validate/common/hooks/post-receive.hook | 293 + validate/common/hooks/pre-commit.hook | 83 + validate/common/hooks/pre-receive.hook | 190 + validate/common/hooks/update.hook | 128 + validate/common/m4/.gitignore | 23 + validate/common/m4/Makefile.am | 41 + validate/common/m4/README | 3 + validate/common/m4/as-ac-expand.m4 | 43 + validate/common/m4/as-auto-alt.m4 | 50 + validate/common/m4/as-compiler-flag.m4 | 96 + validate/common/m4/as-compiler.m4 | 44 + validate/common/m4/as-docbook.m4 | 78 + validate/common/m4/as-gcc-inline-assembly.m4 | 52 + validate/common/m4/as-libtool-tags.m4 | 83 + validate/common/m4/as-libtool.m4 | 46 + validate/common/m4/as-python.m4 | 152 + validate/common/m4/as-version.m4 | 75 + validate/common/m4/ax_create_stdint_h.m4 | 734 ++ validate/common/m4/ax_pthread.m4 | 332 + validate/common/m4/check.m4 | 181 + validate/common/m4/glib-gettext.m4 | 432 + validate/common/m4/gst-arch.m4 | 147 + validate/common/m4/gst-args.m4 | 360 + validate/common/m4/gst-check.m4 | 294 + validate/common/m4/gst-debuginfo.m4 | 46 + validate/common/m4/gst-default.m4 | 120 + validate/common/m4/gst-doc.m4 | 92 + validate/common/m4/gst-dowhile.m4 | 24 + validate/common/m4/gst-error.m4 | 290 + validate/common/m4/gst-feature.m4 | 297 + validate/common/m4/gst-function.m4 | 63 + validate/common/m4/gst-gettext.m4 | 28 + validate/common/m4/gst-glib2.m4 | 130 + validate/common/m4/gst-libxml2.m4 | 52 + validate/common/m4/gst-package-release-datetime.m4 | 89 + validate/common/m4/gst-parser.m4 | 55 + validate/common/m4/gst-platform.m4 | 67 + validate/common/m4/gst-plugin-docs.m4 | 25 + validate/common/m4/gst-plugindir.m4 | 17 + validate/common/m4/gst-valgrind.m4 | 35 + validate/common/m4/gst-x11.m4 | 74 + validate/common/m4/gst.m4 | 36 + validate/common/m4/gtk-doc.m4 | 70 + validate/common/m4/introspection.m4 | 94 + validate/common/m4/orc.m4 | 70 + validate/common/m4/pkg.m4 | 157 + validate/common/mangle-tmpl.py | 165 + validate/common/orc.mak | 76 + validate/common/parallel-subdirs.mak | 13 + validate/common/plugins.xsl | 209 + validate/common/po.mak | 4 + validate/common/release.mak | 34 + validate/common/scangobj-merge.py | 314 + validate/common/update-autogen | 48 + validate/common/update-common | 187 + validate/common/update-readmes | 42 + validate/common/upload-doc.mak | 64 + validate/common/win32.mak | 78 + validate/configure.ac | 348 + validate/data/Makefile.am | 7 + validate/data/gstvalidate.supp | 152 + validate/data/scenarios/Makefile.am | 52 + .../scenarios/adaptive_video_framerate.scenario | 5 + .../adaptive_video_framerate_size.scenario | 7 + .../data/scenarios/adaptive_video_size.scenario | 5 + .../alternate_fast_backward_forward.scenario | 13 + validate/data/scenarios/camerabin_signal.scenario | 4 + .../data/scenarios/change_state_intensive.scenario | 3 + .../disable_subtitle_track_while_paused.scenario | 6 + validate/data/scenarios/fast_backward.scenario | 6 + validate/data/scenarios/fast_forward.scenario | 7 + validate/data/scenarios/force_key_unit.scenario | 4 + validate/data/scenarios/pause_resume.scenario | 6 + validate/data/scenarios/play_15s.scenario | 3 + validate/data/scenarios/reverse_playback.scenario | 2 + .../data/scenarios/scrub_backward_seeking.scenario | 7 + .../scenarios/scrub_backward_seeking_full.scenario | 7 + .../data/scenarios/scrub_forward_seeking.scenario | 5 + .../scenarios/scrub_forward_seeking_full.scenario | 5 + validate/data/scenarios/seek_backward.scenario | 5 + validate/data/scenarios/seek_forward.scenario | 5 + .../data/scenarios/seek_forward_backward.scenario | 9 + validate/data/scenarios/seek_with_stop.scenario | 2 + .../setup_sink_props_max_lateness.scenario | 3 + validate/data/scenarios/simple_seeks.scenario | 4 + .../data/scenarios/switch_audio_track.scenario | 3 + .../switch_audio_track_while_paused.scenario | 11 + .../switch_set_external_subtitle.scenario | 1 + .../data/scenarios/switch_subtitle_track.scenario | 3 + .../switch_subtitle_track_while_paused.scenario | 7 + validate/data/scenarios/update_start.scenario | 2 + validate/data/scenarios/update_stop.scenario | 3 + validate/data/valgrind.config | 1 + validate/docs/.gitignore | 10 + validate/docs/Makefile.am | 13 + validate/docs/launcher/Makefile.am | 177 + validate/docs/launcher/conf.py | 243 + validate/docs/launcher/index.rst | 22 + validate/docs/launcher/launcher.rst | 43 + validate/docs/launcher/modules.rst | 7 + validate/docs/plugins/Makefile.am | 62 + .../docs/plugins/gst-validate-plugins-docs.sgml | 28 + .../plugins/gst-validate-plugins-overrides.txt | 0 .../docs/plugins/gst-validate-plugins-sections.txt | 9 + validate/docs/plugins/gst-validate-plugins.sgml | 24 + validate/docs/plugins/gst-validate-plugins.types | 1 + validate/docs/release.txt | 2 + validate/docs/validate-design.txt | 61 + validate/docs/validate-usage.txt | 90 + validate/docs/validate/.gitignore | 6 + validate/docs/validate/Makefile.am | 101 + validate/docs/validate/envvariables.xml | 188 + validate/docs/validate/gst-validate-docs.sgml | 83 + validate/docs/validate/gst-validate-launcher.xml | 189 + .../docs/validate/gst-validate-media-check.xml | 89 + validate/docs/validate/gst-validate-sections.txt | 92 + .../docs/validate/gst-validate-transcoding.xml | 220 + validate/docs/validate/gst-validate.types | 10 + validate/docs/validate/gst-validate.xml | 139 + validate/docs/validate/scenarios.xml | 143 + validate/docs/version.entities.in | 2 + validate/gst-libs/Makefile.am | 1 + validate/gst-libs/gst/Makefile.am | 3 + validate/gst-libs/gst/video/Makefile.am | 12 + validate/gst-libs/gst/video/gssim.c | 453 + validate/gst-libs/gst/video/gssim.h | 62 + validate/gst-libs/gst/video/gstvalidatessim.c | 972 ++ validate/gst-libs/gst/video/gstvalidatessim.h | 73 + validate/gst-validate-devel.manifest | 5 + validate/gst-validate.doap | 81 + validate/gst-validate.manifest | 5 + validate/gst/Makefile.am | 5 + validate/gst/overrides/Makefile.am | 14 + .../gst/overrides/gst-validate-default-overrides.c | 47 + validate/gst/preload/Makefile.am | 15 + .../gst/preload/gst-validate-monitor-preload.c | 164 + validate/gst/validate/Makefile.am | 122 + validate/gst/validate/gettext.h | 69 + validate/gst/validate/gst-validate-bin-monitor.c | 270 + validate/gst/validate/gst-validate-bin-monitor.h | 84 + .../gst/validate/gst-validate-element-monitor.c | 313 + .../gst/validate/gst-validate-element-monitor.h | 88 + validate/gst/validate/gst-validate-enums.h | 81 + validate/gst/validate/gst-validate-i18n-lib.h | 47 + validate/gst/validate/gst-validate-internal.h | 48 + validate/gst/validate/gst-validate-media-info.c | 1128 ++ validate/gst/validate/gst-validate-media-info.h | 80 + .../gst/validate/gst-validate-monitor-factory.c | 80 + .../gst/validate/gst-validate-monitor-factory.h | 38 + validate/gst/validate/gst-validate-monitor.c | 413 + validate/gst/validate/gst-validate-monitor.h | 130 + .../gst/validate/gst-validate-override-registry.c | 463 + .../gst/validate/gst-validate-override-registry.h | 58 + validate/gst/validate/gst-validate-override.c | 265 + validate/gst/validate/gst-validate-override.h | 107 + validate/gst/validate/gst-validate-pad-monitor.c | 2564 +++++ validate/gst/validate/gst-validate-pad-monitor.h | 140 + .../gst/validate/gst-validate-pipeline-monitor.c | 240 + .../gst/validate/gst-validate-pipeline-monitor.h | 82 + validate/gst/validate/gst-validate-report.c | 891 ++ validate/gst/validate/gst-validate-report.h | 239 + validate/gst/validate/gst-validate-reporter.c | 408 + validate/gst/validate/gst-validate-reporter.h | 119 + validate/gst/validate/gst-validate-runner.c | 531 + validate/gst/validate/gst-validate-runner.h | 91 + validate/gst/validate/gst-validate-scenario.c | 3511 ++++++ validate/gst/validate/gst-validate-scenario.h | 306 + validate/gst/validate/gst-validate-types.h | 30 + validate/gst/validate/gst-validate-utils.c | 736 ++ validate/gst/validate/gst-validate-utils.h | 51 + validate/gst/validate/media-descriptor-parser.c | 508 + validate/gst/validate/media-descriptor-parser.h | 75 + validate/gst/validate/media-descriptor-writer.c | 818 ++ validate/gst/validate/media-descriptor-writer.h | 93 + validate/gst/validate/media-descriptor.c | 461 + validate/gst/validate/media-descriptor.h | 159 + validate/gst/validate/validate.c | 280 + validate/gst/validate/validate.h | 23 + validate/launcher/Makefile.am | 20 + validate/launcher/RangeHTTPServer.py | 294 + validate/launcher/__init__.py | 18 + validate/launcher/apps/Makefile.am | 7 + validate/launcher/apps/__init__.py | 0 validate/launcher/apps/gstvalidate.py | 839 ++ validate/launcher/baseclasses.py | 1719 +++ validate/launcher/config.py.in | 21 + validate/launcher/httpserver.py | 106 + validate/launcher/loggable.py | 1237 +++ validate/launcher/main.py | 529 + validate/launcher/reporters.py | 218 + validate/launcher/utils.py | 249 + validate/launcher/vfb_server.py | 112 + validate/multi-pre-commit.hook | 43 + validate/pkgconfig/Makefile.am | 21 + validate/pkgconfig/gst-validate-uninstalled.pc.in | 12 + validate/pkgconfig/gst-validate.pc.in | 11 + validate/plugins/Makefile.am | 9 + validate/plugins/fault_injection/Makefile.am | 10 + .../plugins/fault_injection/socket_interposer.c | 388 + validate/plugins/gapplication/Makefile.am | 11 + .../plugins/gapplication/gstvalidategapplication.c | 80 + validate/plugins/gtk/Makefile.am | 11 + validate/plugins/gtk/gstvalidategtk.c | 519 + validate/plugins/ssim/Makefile.am | 12 + validate/plugins/ssim/gstvalidatessim.c | 816 ++ validate/pre-commit-python.hook | 82 + validate/tests/Makefile.am | 10 + validate/tests/check/Makefile.am | 98 + validate/tests/check/validate/monitoring.c | 111 + validate/tests/check/validate/overrides.c | 109 + validate/tests/check/validate/padmonitor.c | 1096 ++ validate/tests/check/validate/reporting.c | 315 + validate/tests/check/validate/test-utils.c | 301 + validate/tests/check/validate/test-utils.h | 79 + validate/tools/.gitignore | 8 + validate/tools/Makefile.am | 48 + validate/tools/gst-validate-images-check.c | 114 + validate/tools/gst-validate-launcher.in | 62 + validate/tools/gst-validate-media-check.c | 147 + validate/tools/gst-validate-transcoding.c | 966 ++ validate/tools/gst-validate.c | 635 ++ 260 files changed, 61614 insertions(+) create mode 160000 gst-integration-testsuites create mode 100755 packaging/gst-validate.spec create mode 100755 packaging/gst-validate.spec~ create mode 100644 validate/.gitignore create mode 100644 validate/.gitmodules create mode 100644 validate/AUTHORS create mode 100644 validate/COPYING create mode 100644 validate/ChangeLog create mode 100644 validate/Makefile.am create mode 100644 validate/NEWS create mode 100644 validate/README create mode 100755 validate/autogen.sh create mode 100644 validate/common/ChangeLog create mode 100644 validate/common/MAINTAINERS create mode 100644 validate/common/Makefile.am create mode 100644 validate/common/README create mode 100644 validate/common/README.static-linking create mode 100755 validate/common/autogen.sh.in create mode 100644 validate/common/c-to-xml.py create mode 100755 validate/common/check-exports create mode 100644 validate/common/check.mak create mode 100644 validate/common/coverage/coverage-report-entry.pl create mode 100644 validate/common/coverage/coverage-report.pl create mode 100644 validate/common/coverage/coverage-report.xsl create mode 100644 validate/common/coverage/lcov.mak create mode 100644 validate/common/cruft.mak create mode 100755 validate/common/download-translations create mode 100755 validate/common/extract-release-date-from-doap-file create mode 100644 validate/common/gen-changelog.py create mode 100644 validate/common/gettext.patch create mode 100644 validate/common/glib-gen.mak create mode 100644 validate/common/gst-autogen.sh create mode 100644 validate/common/gst-glib-gen.mak create mode 100755 validate/common/gst-indent create mode 100644 validate/common/gst.supp create mode 100755 validate/common/gstdoc-scangobj create mode 100644 validate/common/gtk-doc-plugins.mak create mode 100644 validate/common/gtk-doc.mak create mode 100755 validate/common/hooks/install-hooks create mode 100755 validate/common/hooks/post-receive.hook create mode 100755 validate/common/hooks/pre-commit.hook create mode 100755 validate/common/hooks/pre-receive.hook create mode 100755 validate/common/hooks/update.hook create mode 100644 validate/common/m4/.gitignore create mode 100644 validate/common/m4/Makefile.am create mode 100644 validate/common/m4/README create mode 100644 validate/common/m4/as-ac-expand.m4 create mode 100644 validate/common/m4/as-auto-alt.m4 create mode 100644 validate/common/m4/as-compiler-flag.m4 create mode 100644 validate/common/m4/as-compiler.m4 create mode 100644 validate/common/m4/as-docbook.m4 create mode 100644 validate/common/m4/as-gcc-inline-assembly.m4 create mode 100644 validate/common/m4/as-libtool-tags.m4 create mode 100644 validate/common/m4/as-libtool.m4 create mode 100644 validate/common/m4/as-python.m4 create mode 100644 validate/common/m4/as-version.m4 create mode 100644 validate/common/m4/ax_create_stdint_h.m4 create mode 100644 validate/common/m4/ax_pthread.m4 create mode 100644 validate/common/m4/check.m4 create mode 100644 validate/common/m4/glib-gettext.m4 create mode 100644 validate/common/m4/gst-arch.m4 create mode 100644 validate/common/m4/gst-args.m4 create mode 100644 validate/common/m4/gst-check.m4 create mode 100644 validate/common/m4/gst-debuginfo.m4 create mode 100644 validate/common/m4/gst-default.m4 create mode 100644 validate/common/m4/gst-doc.m4 create mode 100644 validate/common/m4/gst-dowhile.m4 create mode 100644 validate/common/m4/gst-error.m4 create mode 100644 validate/common/m4/gst-feature.m4 create mode 100644 validate/common/m4/gst-function.m4 create mode 100644 validate/common/m4/gst-gettext.m4 create mode 100644 validate/common/m4/gst-glib2.m4 create mode 100644 validate/common/m4/gst-libxml2.m4 create mode 100644 validate/common/m4/gst-package-release-datetime.m4 create mode 100644 validate/common/m4/gst-parser.m4 create mode 100644 validate/common/m4/gst-platform.m4 create mode 100644 validate/common/m4/gst-plugin-docs.m4 create mode 100644 validate/common/m4/gst-plugindir.m4 create mode 100644 validate/common/m4/gst-valgrind.m4 create mode 100644 validate/common/m4/gst-x11.m4 create mode 100644 validate/common/m4/gst.m4 create mode 100644 validate/common/m4/gtk-doc.m4 create mode 100644 validate/common/m4/introspection.m4 create mode 100644 validate/common/m4/orc.m4 create mode 100644 validate/common/m4/pkg.m4 create mode 100644 validate/common/mangle-tmpl.py create mode 100644 validate/common/orc.mak create mode 100644 validate/common/parallel-subdirs.mak create mode 100644 validate/common/plugins.xsl create mode 100644 validate/common/po.mak create mode 100644 validate/common/release.mak create mode 100755 validate/common/scangobj-merge.py create mode 100755 validate/common/update-autogen create mode 100755 validate/common/update-common create mode 100755 validate/common/update-readmes create mode 100644 validate/common/upload-doc.mak create mode 100644 validate/common/win32.mak create mode 100644 validate/configure.ac create mode 100644 validate/data/Makefile.am create mode 100644 validate/data/gstvalidate.supp create mode 100644 validate/data/scenarios/Makefile.am create mode 100644 validate/data/scenarios/adaptive_video_framerate.scenario create mode 100644 validate/data/scenarios/adaptive_video_framerate_size.scenario create mode 100644 validate/data/scenarios/adaptive_video_size.scenario create mode 100644 validate/data/scenarios/alternate_fast_backward_forward.scenario create mode 100644 validate/data/scenarios/camerabin_signal.scenario create mode 100644 validate/data/scenarios/change_state_intensive.scenario create mode 100644 validate/data/scenarios/disable_subtitle_track_while_paused.scenario create mode 100644 validate/data/scenarios/fast_backward.scenario create mode 100644 validate/data/scenarios/fast_forward.scenario create mode 100644 validate/data/scenarios/force_key_unit.scenario create mode 100644 validate/data/scenarios/pause_resume.scenario create mode 100644 validate/data/scenarios/play_15s.scenario create mode 100644 validate/data/scenarios/reverse_playback.scenario create mode 100644 validate/data/scenarios/scrub_backward_seeking.scenario create mode 100644 validate/data/scenarios/scrub_backward_seeking_full.scenario create mode 100644 validate/data/scenarios/scrub_forward_seeking.scenario create mode 100644 validate/data/scenarios/scrub_forward_seeking_full.scenario create mode 100644 validate/data/scenarios/seek_backward.scenario create mode 100644 validate/data/scenarios/seek_forward.scenario create mode 100644 validate/data/scenarios/seek_forward_backward.scenario create mode 100644 validate/data/scenarios/seek_with_stop.scenario create mode 100644 validate/data/scenarios/setup_sink_props_max_lateness.scenario create mode 100644 validate/data/scenarios/simple_seeks.scenario create mode 100644 validate/data/scenarios/switch_audio_track.scenario create mode 100644 validate/data/scenarios/switch_audio_track_while_paused.scenario create mode 100644 validate/data/scenarios/switch_set_external_subtitle.scenario create mode 100644 validate/data/scenarios/switch_subtitle_track.scenario create mode 100644 validate/data/scenarios/switch_subtitle_track_while_paused.scenario create mode 100644 validate/data/scenarios/update_start.scenario create mode 100644 validate/data/scenarios/update_stop.scenario create mode 100644 validate/data/valgrind.config create mode 100644 validate/docs/.gitignore create mode 100644 validate/docs/Makefile.am create mode 100644 validate/docs/launcher/Makefile.am create mode 100644 validate/docs/launcher/conf.py create mode 100644 validate/docs/launcher/index.rst create mode 100644 validate/docs/launcher/launcher.rst create mode 100644 validate/docs/launcher/modules.rst create mode 100644 validate/docs/plugins/Makefile.am create mode 100644 validate/docs/plugins/gst-validate-plugins-docs.sgml create mode 100644 validate/docs/plugins/gst-validate-plugins-overrides.txt create mode 100644 validate/docs/plugins/gst-validate-plugins-sections.txt create mode 100644 validate/docs/plugins/gst-validate-plugins.sgml create mode 100644 validate/docs/plugins/gst-validate-plugins.types create mode 100644 validate/docs/release.txt create mode 100644 validate/docs/validate-design.txt create mode 100644 validate/docs/validate-usage.txt create mode 100644 validate/docs/validate/.gitignore create mode 100644 validate/docs/validate/Makefile.am create mode 100644 validate/docs/validate/envvariables.xml create mode 100644 validate/docs/validate/gst-validate-docs.sgml create mode 100644 validate/docs/validate/gst-validate-launcher.xml create mode 100644 validate/docs/validate/gst-validate-media-check.xml create mode 100644 validate/docs/validate/gst-validate-sections.txt create mode 100644 validate/docs/validate/gst-validate-transcoding.xml create mode 100644 validate/docs/validate/gst-validate.types create mode 100644 validate/docs/validate/gst-validate.xml create mode 100644 validate/docs/validate/scenarios.xml create mode 100644 validate/docs/version.entities.in create mode 100644 validate/gst-libs/Makefile.am create mode 100644 validate/gst-libs/gst/Makefile.am create mode 100644 validate/gst-libs/gst/video/Makefile.am create mode 100644 validate/gst-libs/gst/video/gssim.c create mode 100644 validate/gst-libs/gst/video/gssim.h create mode 100644 validate/gst-libs/gst/video/gstvalidatessim.c create mode 100644 validate/gst-libs/gst/video/gstvalidatessim.h create mode 100644 validate/gst-validate-devel.manifest create mode 100644 validate/gst-validate.doap create mode 100644 validate/gst-validate.manifest create mode 100644 validate/gst/Makefile.am create mode 100644 validate/gst/overrides/Makefile.am create mode 100644 validate/gst/overrides/gst-validate-default-overrides.c create mode 100644 validate/gst/preload/Makefile.am create mode 100644 validate/gst/preload/gst-validate-monitor-preload.c create mode 100644 validate/gst/validate/Makefile.am create mode 100644 validate/gst/validate/gettext.h create mode 100644 validate/gst/validate/gst-validate-bin-monitor.c create mode 100644 validate/gst/validate/gst-validate-bin-monitor.h create mode 100644 validate/gst/validate/gst-validate-element-monitor.c create mode 100644 validate/gst/validate/gst-validate-element-monitor.h create mode 100644 validate/gst/validate/gst-validate-enums.h create mode 100644 validate/gst/validate/gst-validate-i18n-lib.h create mode 100644 validate/gst/validate/gst-validate-internal.h create mode 100644 validate/gst/validate/gst-validate-media-info.c create mode 100644 validate/gst/validate/gst-validate-media-info.h create mode 100644 validate/gst/validate/gst-validate-monitor-factory.c create mode 100644 validate/gst/validate/gst-validate-monitor-factory.h create mode 100644 validate/gst/validate/gst-validate-monitor.c create mode 100644 validate/gst/validate/gst-validate-monitor.h create mode 100644 validate/gst/validate/gst-validate-override-registry.c create mode 100644 validate/gst/validate/gst-validate-override-registry.h create mode 100644 validate/gst/validate/gst-validate-override.c create mode 100644 validate/gst/validate/gst-validate-override.h create mode 100644 validate/gst/validate/gst-validate-pad-monitor.c create mode 100644 validate/gst/validate/gst-validate-pad-monitor.h create mode 100644 validate/gst/validate/gst-validate-pipeline-monitor.c create mode 100644 validate/gst/validate/gst-validate-pipeline-monitor.h create mode 100644 validate/gst/validate/gst-validate-report.c create mode 100644 validate/gst/validate/gst-validate-report.h create mode 100644 validate/gst/validate/gst-validate-reporter.c create mode 100644 validate/gst/validate/gst-validate-reporter.h create mode 100644 validate/gst/validate/gst-validate-runner.c create mode 100644 validate/gst/validate/gst-validate-runner.h create mode 100644 validate/gst/validate/gst-validate-scenario.c create mode 100644 validate/gst/validate/gst-validate-scenario.h create mode 100644 validate/gst/validate/gst-validate-types.h create mode 100644 validate/gst/validate/gst-validate-utils.c create mode 100644 validate/gst/validate/gst-validate-utils.h create mode 100644 validate/gst/validate/media-descriptor-parser.c create mode 100644 validate/gst/validate/media-descriptor-parser.h create mode 100644 validate/gst/validate/media-descriptor-writer.c create mode 100644 validate/gst/validate/media-descriptor-writer.h create mode 100644 validate/gst/validate/media-descriptor.c create mode 100644 validate/gst/validate/media-descriptor.h create mode 100644 validate/gst/validate/validate.c create mode 100644 validate/gst/validate/validate.h create mode 100644 validate/launcher/Makefile.am create mode 100644 validate/launcher/RangeHTTPServer.py create mode 100644 validate/launcher/__init__.py create mode 100644 validate/launcher/apps/Makefile.am create mode 100644 validate/launcher/apps/__init__.py create mode 100644 validate/launcher/apps/gstvalidate.py create mode 100644 validate/launcher/baseclasses.py create mode 100644 validate/launcher/config.py.in create mode 100644 validate/launcher/httpserver.py create mode 100644 validate/launcher/loggable.py create mode 100644 validate/launcher/main.py create mode 100644 validate/launcher/reporters.py create mode 100644 validate/launcher/utils.py create mode 100644 validate/launcher/vfb_server.py create mode 100755 validate/multi-pre-commit.hook create mode 100644 validate/pkgconfig/Makefile.am create mode 100644 validate/pkgconfig/gst-validate-uninstalled.pc.in create mode 100644 validate/pkgconfig/gst-validate.pc.in create mode 100644 validate/plugins/Makefile.am create mode 100644 validate/plugins/fault_injection/Makefile.am create mode 100644 validate/plugins/fault_injection/socket_interposer.c create mode 100644 validate/plugins/gapplication/Makefile.am create mode 100644 validate/plugins/gapplication/gstvalidategapplication.c create mode 100644 validate/plugins/gtk/Makefile.am create mode 100644 validate/plugins/gtk/gstvalidategtk.c create mode 100644 validate/plugins/ssim/Makefile.am create mode 100644 validate/plugins/ssim/gstvalidatessim.c create mode 100755 validate/pre-commit-python.hook create mode 100644 validate/tests/Makefile.am create mode 100644 validate/tests/check/Makefile.am create mode 100644 validate/tests/check/validate/monitoring.c create mode 100644 validate/tests/check/validate/overrides.c create mode 100644 validate/tests/check/validate/padmonitor.c create mode 100644 validate/tests/check/validate/reporting.c create mode 100644 validate/tests/check/validate/test-utils.c create mode 100644 validate/tests/check/validate/test-utils.h create mode 100644 validate/tools/.gitignore create mode 100644 validate/tools/Makefile.am create mode 100644 validate/tools/gst-validate-images-check.c create mode 100644 validate/tools/gst-validate-launcher.in create mode 100644 validate/tools/gst-validate-media-check.c create mode 100644 validate/tools/gst-validate-transcoding.c create mode 100644 validate/tools/gst-validate.c diff --git a/gst-integration-testsuites b/gst-integration-testsuites new file mode 160000 index 0000000..c7f00c5 --- /dev/null +++ b/gst-integration-testsuites @@ -0,0 +1 @@ +Subproject commit c7f00c599c5b60cc4ffc238511fb2849a2ad514f diff --git a/packaging/gst-validate.spec b/packaging/gst-validate.spec new file mode 100755 index 0000000..4c193e8 --- /dev/null +++ b/packaging/gst-validate.spec @@ -0,0 +1,95 @@ +Name: gst-validate +Summary: GStreamer streaming media framework base plug-ins +Version: 1.4.5 +Release: 0 +Group: Applications/Multimedia +License: LGPL +Source0: %{name}-%{version}.tar.gz +Requires(post): /sbin/ldconfig +Requires(postun): /sbin/ldconfig +BuildRequires: pkgconfig(glib-2.0) +BuildRequires: pkgconfig(gstreamer-1.0) +BuildRequires: pkgconfig(gstreamer-plugins-base-1.0) +BuildRequires: intltool +BuildRequires: python + +%description +GStreamer is a streaming media framework, based on graphs of filters which +operate on media data. Applications using this library can do anything +from real-time sound processing to playing videos, and just about anything +else media-related. Its plugin-based architecture means that new data +types or processing capabilities can be added simply by installing new +plug-ins. + + +%package devel +Summary: GStreamer Plugin Library Headers +Group: Development/Libraries +Requires: %{name} = %{version}-%{release} +Obsoletes: gstreamer-validate-devel < %{version}-%{release} +Provides: gstreamer-validate-devel = %{version}-%{release} + +%description devel +GStreamer Plugins Base library development and header files. + + +%prep +%setup -q + +%build +cd validate +%autogen --noconfigure + +export CFLAGS+=" -Wall -g -fPIC" + +%configure --prefix=/usr\ + +make %{?jobs:-j%jobs} + +%install +cd validate +rm -rf %{buildroot} +mkdir -p %{buildroot}/usr/share/license +cp COPYING %{buildroot}/usr/share/license/%{name} +%make_install +rm -rf %{buildroot}/tmp/dump + +%post -p /sbin/ldconfig + +%postun -p /sbin/ldconfig + +%files +%manifest validate/gst-validate.manifest +%defattr(-,root,root,-) +%doc validate/AUTHORS validate/COPYING validate/README validate/gst-validate.doap + +/usr/share/license/%{name} + +%{_bindir}/gst-validate-1.0 +%{_bindir}/gst-validate-launcher +%{_bindir}/gst-validate-media-check-1.0 +%{_bindir}/gst-validate-transcoding-1.0 + +/usr/share/gstreamer-1.0/validate/ +/usr/include/gstreamer-1.0/gst/validate/ + +# non-core plugins without external dependencies +%{_libdir}/libgstvalidate-1.0.so +%{_libdir}/libgstvalidate-1.0.so.0 +%{_libdir}/libgstvalidate-1.0.so.0.0.0 +%{_libdir}/libgstvalidate_preload-1.0.so +%{_libdir}/libgstvalidate_preload-1.0.so.0 +%{_libdir}/libgstvalidate_preload-1.0.so.0.0.0 +%{_libdir}/libgstvalidate-default-overrides-1.0.so +%{_libdir}/libgstvalidate-default-overrides-1.0.so.0 +%{_libdir}/libgstvalidate-default-overrides-1.0.so.0.0.0 +%{_libdir}/gstreamer-1.0/validate/libgstvalidategapplication.so +%{_libdir}/gstreamer-1.0/validate/libgstvalidatefaultinjection.so +%{_libdir}/gst-validate-launcher/python/ + +%files devel +%manifest validate/gst-validate-devel.manifest +%defattr(-,root,root,-) + +# pkg-config files +%{_libdir}/pkgconfig/gst-validate-1.0.pc diff --git a/packaging/gst-validate.spec~ b/packaging/gst-validate.spec~ new file mode 100755 index 0000000..bf8c49d --- /dev/null +++ b/packaging/gst-validate.spec~ @@ -0,0 +1,96 @@ +Name: gst-validate +Summary: GStreamer streaming media framework base plug-ins +Version: 1.4.5 +Release: 0 +Group: Applications/Multimedia +License: LGPL +Source0: %{name}-%{version}.tar.gz +Requires(post): /sbin/ldconfig +Requires(postun): /sbin/ldconfig +BuildRequires: pkgconfig(glib-2.0) +BuildRequires: pkgconfig(gstreamer-1.0) +BuildRequires: pkgconfig(gstreamer-plugins-base-1.0) +BuildRequires: intltool +BuildRequires: python +BuildRequires: pkgconfig(libpyg-2.0) + +%description +GStreamer is a streaming media framework, based on graphs of filters which +operate on media data. Applications using this library can do anything +from real-time sound processing to playing videos, and just about anything +else media-related. Its plugin-based architecture means that new data +types or processing capabilities can be added simply by installing new +plug-ins. + + +%package devel +Summary: GStreamer Plugin Library Headers +Group: Development/Libraries +Requires: %{name} = %{version}-%{release} +Obsoletes: gstreamer-validate-devel < %{version}-%{release} +Provides: gstreamer-validate-devel = %{version}-%{release} + +%description devel +GStreamer Plugins Base library development and header files. + + +%prep +%setup -q + +%build +cd validate +%autogen --noconfigure + +export CFLAGS+=" -Wall -g -fPIC" + +%configure --prefix=/usr\ + +make %{?jobs:-j%jobs} + +%install +cd validate +rm -rf %{buildroot} +mkdir -p %{buildroot}/usr/share/license +cp COPYING %{buildroot}/usr/share/license/%{name} +%make_install +rm -rf %{buildroot}/tmp/dump + +%post -p /sbin/ldconfig + +%postun -p /sbin/ldconfig + +%files +%manifest validate/gst-validate.manifest +%defattr(-,root,root,-) +%doc validate/AUTHORS validate/COPYING validate/README validate/gst-validate.doap + +/usr/share/license/%{name} + +%{_bindir}/gst-validate-1.0 +%{_bindir}/gst-validate-launcher +%{_bindir}/gst-validate-media-check-1.0 +%{_bindir}/gst-validate-transcoding-1.0 + +/usr/share/gstreamer-1.0/validate/ +/usr/include/gstreamer-1.0/gst/validate/ + +# non-core plugins without external dependencies +%{_libdir}/libgstvalidate-1.0.so +%{_libdir}/libgstvalidate-1.0.so.0 +%{_libdir}/libgstvalidate-1.0.so.0.0.0 +%{_libdir}/libgstvalidate_preload-1.0.so +%{_libdir}/libgstvalidate_preload-1.0.so.0 +%{_libdir}/libgstvalidate_preload-1.0.so.0.0.0 +%{_libdir}/libgstvalidate-default-overrides-1.0.so +%{_libdir}/libgstvalidate-default-overrides-1.0.so.0 +%{_libdir}/libgstvalidate-default-overrides-1.0.so.0.0.0 +%{_libdir}/gstreamer-1.0/validate/libgstvalidategapplication.so +%{_libdir}/gstreamer-1.0/validate/libgstvalidatefaultinjection.so +%{_libdir}/gst-validate-launcher/python/ + +%files devel +%manifest validate/gst-validate-devel.manifest +%defattr(-,root,root,-) + +# pkg-config files +%{_libdir}/pkgconfig/gst-validate-1.0.pc diff --git a/validate/.gitignore b/validate/.gitignore new file mode 100644 index 0000000..251025b --- /dev/null +++ b/validate/.gitignore @@ -0,0 +1,51 @@ +*.[oa] +*.pyc +*.gcda +*.gcno +*.la +*.lo +*.loT +*.sw[po] +*.tar.* +*~ +.deps +.libs +ABOUT-NLS +INSTALL +Makefile +Makefile.in +aclocal.m4 +autom4te.cache +autoregen.sh +compile +config.guess +config.h +config.h.in +config.log +config.rpath +config.status +config.sub +configure +depcomp +install-sh +libtool +ltmain.sh +missing +py-compile +stamp-h1 +tags +test-driver +stamp-h.in +.dirstamp +*.gir +*.typefind +*.orig + +.arcconfig +/m4/*m4 + +/po +*docs/launcher/html +*tools/gst-validate-launcher + +/launcher/config.py diff --git a/validate/.gitmodules b/validate/.gitmodules new file mode 100644 index 0000000..e69de29 diff --git a/validate/AUTHORS b/validate/AUTHORS new file mode 100644 index 0000000..e69de29 diff --git a/validate/COPYING b/validate/COPYING new file mode 100644 index 0000000..8add30a --- /dev/null +++ b/validate/COPYING @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library 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.1 of the License, or (at your option) any later version. + + This library 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; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/validate/ChangeLog b/validate/ChangeLog new file mode 100644 index 0000000..c9ba488 --- /dev/null +++ b/validate/ChangeLog @@ -0,0 +1,10872 @@ +=== release 1.5.2 === + +2015-06-24 Thibault Saunier + + * configure.ac: + releasing 1.5.2 + +2015-06-24 16:06:06 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate:scenario: Reset seeked_in_paused when wrong position detected + + Lower some debug output to LOG + +2015-06-18 11:09:26 +0200 Thibault Saunier + + * validate/gst-libs/gst/video/gstvalidatessim.c: + * validate/plugins/ssim/gstvalidatessim.c: + * validate/tools/gst-validate-images-check.c: + validate:ssim: Inform about min average and min minimum similarities + +2015-06-14 22:44:26 +0100 Tim-Philipp Müller + + * validate/gst/validate/media-descriptor-writer.c: + * validate/tools/gst-validate-media-check.c: + validate: spelling fixes + analize != analyze + +2015-05-29 16:45:25 +0900 Wonchul Lee + + * validate/tools/gst-validate-media-check.c: + validate: media-check: add newline to end of print statements + https://bugzilla.gnome.org/show_bug.cgi?id=750089 + +2015-06-09 09:10:42 +0900 Wonchul Lee + + * validate/tools/gst-validate-media-check.c: + validate: media-check: fix double unref in error code path + Writer would get unrefed twice when it could not parse the file. + https://bugzilla.gnome.org/show_bug.cgi?id=750606 + +2015-06-13 19:25:17 +0100 Tim-Philipp Müller + + * validate/tools/Makefile.am: + validate: tools: fix build + /usr/bin/ld: gst-validate-images-check.o: undefined reference to symbol 'gst_init' + /home/tpm/gst/glib-master/gstreamer/gst/.libs/libgstreamer-1.0.so.0: error adding symbols: DSO missing from command line + +2015-06-12 12:10:55 +0200 Thibault Saunier + + * validate/gst-libs/gst/video/gstvalidatessim.c: + * validate/plugins/ssim/gstvalidatessim.c: + validate:ssim: Make position reporting parseable by the launcher + +2015-06-12 11:00:54 +0200 Thibault Saunier + + * validate/plugins/ssim/gstvalidatessim.c: + validate:ssim: Print better information about execution + +2015-06-12 10:59:28 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-report.c: + validate: print REPORTER->name when passed as source in validate_printf + +2015-06-12 11:17:43 +0200 Thibault Saunier + + * validate/launcher/baseclasses.py: + validate:launcher: Avoid printing twice env variables + When printing test command. + +2015-06-09 10:52:21 +0200 Thibault Saunier + + * validate/tools/Makefile.am: + validate:tools: Cleanup Makefile.am + Removing useless CFLAGS and LIBS + +2015-06-09 11:14:58 +0900 Wonchul Lee + + * validate/gst/validate/media-descriptor-writer.c: + validate:media-descriptor-writer: cleanup get tag code + https://bugzilla.gnome.org/show_bug.cgi?id=750609 + +2015-06-08 18:48:30 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-override.h: + validate: Do not define GstValidateOverride type twice + +2015-06-08 18:20:33 +0200 Thibault Saunier + + * validate/Makefile.am: + * validate/data/Makefile.am: + * validate/docs/plugins/Makefile.am: + * validate/docs/plugins/gst-validate-plugins-overrides.txt: + * validate/tools/Makefile.am: + * validate/tools/gst-validate-images-check.c: + validate: Fix make distcheck + +2015-06-08 17:11:51 +0200 Thibault Saunier + + * validate/configure.ac: + * validate/docs/Makefile.am: + * validate/docs/plugins/Makefile.am: + * validate/docs/plugins/gst-validate-plugins-docs.sgml: + * validate/docs/plugins/gst-validate-plugins-sections.txt: + * validate/docs/plugins/gst-validate-plugins.sgml: + * validate/docs/plugins/gst-validate-plugins.types: + * validate/docs/version.entities: + * validate/docs/version.entities.in: + * validate/plugins/Makefile.am: + validate: Generate documentation for Validate plugins + Summary: Depends on D215 + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D216 + +2015-06-08 17:10:50 +0200 Thibault Saunier + + * validate/Makefile.am: + * validate/configure.ac: + * validate/plugins/ssim/Makefile.am: + * validate/plugins/ssim/gstvalidatessim.c: + validate: Add a validate ssim plugin + Summary: + + Bump gst-video dependency to 1.4 as we need GstVideoConvert + Depends on D213: validate: Mark gst_validate_report a G_GNUC_PRINTF + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D215 + +2015-06-03 12:43:52 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-pad-monitor.c: + * validate/gst/validate/gst-validate-reporter.c: + * validate/gst/validate/gst-validate-reporter.h: + * validate/gst/validate/media-descriptor.c: + validate: Mark gst_validate_report a G_GNUC_PRINTF + Summary: + And fix the issue it raised + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D213 + Depends on D211 + +2015-05-25 13:41:04 +0200 Thibault Saunier + + * validate/Makefile.am: + * validate/configure.ac: + * validate/gst-libs/Makefile.am: + * validate/gst-libs/gst/Makefile.am: + * validate/gst-libs/gst/video/Makefile.am: + * validate/gst-libs/gst/video/gssim.c: + * validate/gst-libs/gst/video/gssim.h: + * validate/gst-libs/gst/video/gstvalidatessim.c: + * validate/gst-libs/gst/video/gstvalidatessim.h: + * validate/tools/.gitignore: + * validate/tools/Makefile.am: + * validate/tools/gst-validate-images-check.c: + validate: Add a gst-validate-images-check tool + Summary: + That is a new tool that uses ssim algorithm to compare images + + Add a GstValidateVideo internal library adding an helper Gssim class + Depends on D210 + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D211 + +2015-05-27 19:35:15 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-utils.c: + * validate/gst/validate/gst-validate-utils.h: + validate:utils: Add a utility to get a GstClockTime from a structure + Summary: + Properly handling the different types that can represent ClockTime + Make use of it in gst_validate_action_get_clocktime + API: gst_validate_utils_get_clocktime + Depends on D209 + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D210 + +2015-05-26 18:45:45 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-internal.h: + * validate/gst/validate/gst-validate-override-registry.c: + * validate/gst/validate/validate.c: + validate: Properly clear the overrides registry on deinit + Summary: Depends on D208 + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D209 + +2015-05-26 13:58:15 +0200 Thibault Saunier + + * validate/gst/validate/validate.c: + * validate/gst/validate/validate.h: + validate: Add a way to check whether Validate is initialized + Summary: + API: + gst_validate_is_initialized + Depends on D207 + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D208 + +2015-05-26 15:57:29 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-monitor.c: + * validate/gst/validate/gst-validate-override.c: + * validate/gst/validate/gst-validate-override.h: + validate:override: Add a vmethod to check whether a monitor can attach it + Summary: Depends on D206 + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D207 + +2015-05-26 12:04:02 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-override-registry.c: + validate:override-registry: Make use of gst_validate_element_has_klass + Summary: + + Fix a minor mixup bug between klass_overrides and name_overrides + Depends on D205 + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D206 + +2015-05-26 12:03:25 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-monitor.c: + * validate/gst/validate/gst-validate-override.c: + * validate/gst/validate/gst-validate-override.h: + * validate/gst/validate/gst-validate-reporter.c: + validate:override: Make overrides GObjects + Summary: + This way we can subclass them getting a proper + context in the various override methods. + Depends on D204 + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D205 + +2015-05-25 18:52:34 +0200 Thibault Saunier + + * validate/Makefile.am: + * validate/configure.ac: + * validate/gst/Makefile.am: + * validate/gst/plugins/Makefile.am: + * validate/gst/plugins/fault_injection/Makefile.am: + * validate/gst/plugins/fault_injection/socket_interposer.c: + * validate/gst/plugins/gapplication/Makefile.am: + * validate/gst/plugins/gapplication/gstvalidategapplication.c: + * validate/gst/plugins/gtk/Makefile.am: + * validate/gst/plugins/gtk/gstvalidategtk.c: + * validate/plugins/Makefile.am: + * validate/plugins/fault_injection/Makefile.am: + * validate/plugins/fault_injection/socket_interposer.c: + * validate/plugins/gapplication/Makefile.am: + * validate/plugins/gapplication/gstvalidategapplication.c: + * validate/plugins/gtk/Makefile.am: + * validate/plugins/gtk/gstvalidategtk.c: + validate: Move plugins to the toplevel directory + Summary: + Otherwise we end up with circular / complicated dependencies between + Validate, its libraries, and the plugins + Depends on D203 + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D204 + +2015-05-27 16:41:00 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate:scenario: Round up computed ClockTime values + Otherwise we end up with rounding error and instead of + seeking to 0.1 we seek to 0.09999999999 for example + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D203 + +2015-05-27 13:18:33 +0200 Thibault Saunier + + * validate/gst/preload/gst-validate-monitor-preload.c: + * validate/gst/validate/gst-validate-reporter.c: + * validate/gst/validate/gst-validate-runner.c: + * validate/gst/validate/gst-validate-runner.h: + * validate/tools/gst-validate-media-check.c: + * validate/tools/gst-validate-transcoding.c: + * validate/tools/gst-validate.c: + validate:runner: Add a method to force exiting the runner + This method is similar to runner_printf() but can be used + only once. The user needs to make sure all the pipeline + are in NULL state when this is called. + The method emits a "STOPPING" signal and at that point + overrides or monitors should do extra processing/checks if + needed. + + Make use of it everywhere where it makes sense. + API: + gst_validate_runner_exit + GstValidateRunner::stopping signal + +2015-06-02 20:25:56 -0400 Olivier Crête + + * validate/gst/validate/gst-validate-pad-monitor.c: + * validate/gst/validate/gst-validate-pipeline-monitor.c: + * validate/gst/validate/gst-validate-pipeline-monitor.h: + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-report.h: + * validate/tests/check/validate/padmonitor.c: + pad-monitor: Check that an ERROR GstMessage has been posted on GST_FLOW_ERROR + Summary: + Before returning GST_FLOW_ERROR, an element must post an ERROR GstMessage, + enforce that. + Reviewers: thiblahute, Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D201 + +2015-06-03 11:49:58 +0100 Luis de Bethencourt + + * validate/gst/validate/media-descriptor-writer.c: + validate: remove unused assignment + Reviewers: thiblahute + Differential Revision: http://phabricator.freedesktop.org/D202 + +2015-06-02 16:46:15 -0400 Olivier Crête + + * validate/gst/validate/gst-validate-utils.c: + Revert "validate-utils: simplify _read_builtin ()" + This breaks the fast_forward scenario parsing. + This reverts commit 0cfff156b1d7013174652cdd25d3ad3f0571813e. + +2015-05-29 17:40:26 +0100 Luis de Bethencourt + + * validate/gst/validate/gst-validate-utils.c: + validate-utils: clean error handling in _file_get_lines () + +2015-05-29 16:29:44 +0100 Luis de Bethencourt + + * validate/gst/validate/gst-validate-utils.c: + validate-utils: simplify _read_builtin () + +2015-05-29 15:40:04 +0100 Vineeth T M + + * validate/gst/validate/gst-validate-utils.c: + validate-utils: sqrt(-1.0) leads to undefined result + Using sqrt of -1 is not valid and leads to undefined results. + When comparing the return value of the fucntion in validate-scenario, + it is being checked with ret == -1, so it makes sense to just return -1 in error case. + https://bugzilla.gnome.org/show_bug.cgi?id=748389 + +2015-05-20 13:57:55 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate:scenario: Make sure to actually test position to execute actions + +2015-05-15 14:45:04 +0200 Guillaume Desmottes + + * validate/data/gstvalidate.supp: + validate: add valgrind ignore supps for theora encoder + Reviewers: thiblahute + Differential Revision: http://phabricator.freedesktop.org/D181 + +2015-05-15 14:26:35 +0200 Guillaume Desmottes + + * validate/data/gstvalidate.supp: + validate: add more H264 valgrind supp rules + Reviewers: thiblahute + Differential Revision: http://phabricator.freedesktop.org/D180 + +2015-05-15 12:57:49 +0200 Thibault Saunier + + * validate/gst/plugins/gtk/gstvalidategtk.c: + validate:gtk: Use event->type directly + gdk_event_get_event_type was introduced in Gtk 3.10 only + https://bugzilla.gnome.org/show_bug.cgi?id=749421 + +2015-05-14 17:43:40 +0200 Thibault Saunier + + * validate/launcher/baseclasses.py: + validate:launcher: extra_env_variables is a dictionnary + +2015-05-13 15:30:23 +0200 Thibault Saunier + + * validate/launcher/baseclasses.py: + validate:launcher: Always set the protocol when creating a GstValidateMediaDescriptor + Summary: Depends on D174 + Reviewers: Mathieu_Du, gdesmott + Differential Revision: http://phabricator.freedesktop.org/D175 + +2015-05-13 15:29:43 +0200 Thibault Saunier + + * validate/launcher/apps/gstvalidate.py: + * validate/launcher/baseclasses.py: + validate:launcher: Allow passing extra env variables to the tests + Summary: Depends on D173 + Reviewers: Mathieu_Du, gdesmott + Differential Revision: http://phabricator.freedesktop.org/D174 + +2015-05-13 15:27:08 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-override-registry.c: + validate: overrides: Allow using regex for named overrides + Summary: + And minor fixes + Depends on D172 + Reviewers: Mathieu_Du, gdesmott + Differential Revision: http://phabricator.freedesktop.org/D173 + +2015-05-13 12:18:18 +0200 Thibault Saunier + + * validate/gst/plugins/gapplication/gstvalidategapplication.c: + * validate/gst/plugins/gtk/gstvalidategtk.c: + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + validate:scenario: Handle action execution after pipeline destruction + Summary: + It is possible to keep executing actions after the pipeline + has been destroyed. + API: + GST_VALIDATE_ACTION_TYPE_DOESNT_NEED_PIPELINE + Depends on D171 + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D172 + +2015-05-13 12:16:57 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.h: + validate:scenario: Add a macro to get ActionType from an Action + Summary: Depends on D170 + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D171 + +2015-05-13 12:13:17 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate:scenario: Factor out code to check position + Summary: + Making simpler to follow the execute_next_action function. + Depends on D169 + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D170 + +2015-05-13 11:27:25 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-pipeline-monitor.c: + validate:pipeline-monitor: Stop printing position when not possible + Summary: + If from anything >= PAUSED to anything <= READY we can not query + pipeline position, so do not try to. + Depends on D168 + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D169 + +2015-05-13 11:20:42 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate:scenario: Rename 'get_position_id' to 'execute_actions_source_id' + Summary: Depends on D167 + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D168 + +2015-05-12 12:07:13 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate: Do not unref twice the same list + Summary: Depends on D166 + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D167 + +2015-05-12 10:58:19 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate:scenario: Add a way to specify the pipeline on which a scenario applies + Summary: + From within the scenario itself. + Depends on D165 + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D166 + +2015-05-12 12:04:52 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate: Avoid depending on Gst 1.4 + Summary: Depends on D117 + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D165 + +2015-04-21 15:29:15 +0200 Thibault Saunier + + * validate/configure.ac: + * validate/gst/plugins/Makefile.am: + * validate/gst/plugins/gtk/Makefile.am: + * validate/gst/plugins/gtk/gstvalidategtk.c: + validate: Add a gtk plugins that implements action types relative to Gtk + Summary: + Currently the only supported action is gtk-put-event allowing press and + release keyboard keys. + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D117 + +2015-05-12 09:55:58 +0200 Thibault Saunier + + * validate/autogen.sh: + validate: Always git submodule update from the toplevel directory + Otherwise it fails with older git versions + +2015-05-09 16:28:20 +0200 Emanuele Aina + + * validate/docs/validate/Makefile.am: + * validate/docs/validate/command-line-tools.xml: + * validate/docs/validate/envvariables.xml: + * validate/docs/validate/gst-validate-docs.sgml: + * validate/docs/validate/gst-validate-launcher.xml: + * validate/docs/validate/gst-validate-media-check.xml: + * validate/docs/validate/gst-validate-transcoding.xml: + * validate/docs/validate/gst-validate.xml: + * validate/docs/validate/scenarios.xml: + validate: Reshape documentation + Fix some errors, use more Docbook tags and split each command reference + in its own file. + https://bugzilla.gnome.org/show_bug.cgi?id=749162 + +2015-05-09 16:23:06 +0200 Emanuele Aina + + * validate/autogen.sh: + validate: Go back to the validate dir after submodule init + https://bugzilla.gnome.org/show_bug.cgi?id=749162 + +2015-05-11 17:08:37 +0200 Guillaume Desmottes + + * validate/data/gstvalidate.supp: + validate: ignore x264 valgrind errors + Summary: The x264 code is pretty hardcore so I just opened a bug for now. + Reviewers: thiblahute + Differential Revision: http://phabricator.freedesktop.org/D164 + +2015-05-08 16:33:50 +0200 Guillaume Desmottes + + * validate/data/Makefile.am: + * validate/data/valgrind.config: + * validate/gst/validate/gst-validate-element-monitor.c: + * validate/launcher/baseclasses.py: + validate: disable QOS features when running with valgrind + Reviewers: thiblahute + Differential Revision: http://phabricator.freedesktop.org/D156 + +2015-05-11 14:24:32 +0200 Guillaume Desmottes + + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-utils.c: + * validate/gst/validate/gst-validate-utils.h: + validate: move element_has_klass() to utils + Reviewers: thiblahute + Differential Revision: http://phabricator.freedesktop.org/D163 + +2015-05-11 12:22:25 +0200 Guillaume Desmottes + + * validate/launcher/apps/gstvalidate.py: + * validate/launcher/baseclasses.py: + * validate/launcher/utils.py: + validate: rename get_valgrind_suppression_file() + Summary: + This function is actually not specific to valgrind so we can make it more + generic. + Reviewers: thiblahute + Differential Revision: http://phabricator.freedesktop.org/D162 + +2015-05-11 12:01:56 +0200 Guillaume Desmottes + + * validate/gst/validate/validate.c: + validate: allow to pass more than one file to GST_VALIDATE_CONFIG + Reviewers: thiblahute + Differential Revision: http://phabricator.freedesktop.org/D161 + +2015-05-11 11:47:47 +0200 Guillaume Desmottes + + * validate/gst/validate/validate.c: + validate: gst_validate_plugin_get_config() return 'core' conf if plugin is NULL + Reviewers: thiblahute + Differential Revision: http://phabricator.freedesktop.org/D160 + +2015-05-11 13:54:15 +0200 Guillaume Desmottes + + * validate/gst/validate/validate.c: + * validate/gst/validate/validate.h: + * validate/tests/check/validate/monitoring.c: + * validate/tests/check/validate/overrides.c: + * validate/tests/check/validate/padmonitor.c: + * validate/tests/check/validate/reporting.c: + * validate/tools/gst-validate-media-check.c: + * validate/tools/gst-validate-transcoding.c: + * validate/tools/gst-validate.c: + validate: add gst_validate_deinit() + Reviewers: thiblahute + Differential Revision: http://phabricator.freedesktop.org/D159 + +2015-05-11 14:25:49 +0200 Guillaume Desmottes + + * validate/gst/validate/validate.c: + validate: don't leak not maching config structures + Reviewers: thiblahute + Differential Revision: http://phabricator.freedesktop.org/D158 + +2015-05-11 11:08:36 +0200 Guillaume Desmottes + + * validate/gst/validate/validate.c: + validate: factor out create_config() + Reviewers: thiblahute + Differential Revision: http://phabricator.freedesktop.org/D157 + +2015-05-08 16:28:11 +0200 Guillaume Desmottes + + * validate/gst/validate/gst-validate-scenario.c: + validate: display debug info when stopping because EOS + Summary: + Useful to know if we are executing the 'stop' command provided by the scenario + or not. + Reviewers: thiblahute + Differential Revision: http://phabricator.freedesktop.org/D155 + +2015-05-11 19:40:49 +0200 Thibault Saunier + + * .gitignore: + Update .gitignore + +2015-05-07 11:19:57 +0200 Guillaume Desmottes + + * validate/gst/validate/gst-validate-pad-monitor.c: + validate: unref last_caps when destroying pad monitor + Reviewers: thiblahute + Reviewed By: thiblahute + Differential Revision: http://phabricator.freedesktop.org/D150 + +2015-05-05 15:59:18 +0200 Guillaume Desmottes + + * validate/data/scenarios/reverse_playback.scenario: + validate: fix typo in reverse_playback.scenario + Reviewers: thiblahute + Differential Revision: http://phabricator.freedesktop.org/D148 + +2015-05-05 12:46:38 +0200 Guillaume Desmottes + + * validate/gst/validate/gst-validate-scenario.c: + validate: initialize position + Summary: Fix invalid read when executing without having the actual position. + Reviewers: thiblahute + Differential Revision: http://phabricator.freedesktop.org/D147 + +2015-05-05 09:32:53 +0200 Guillaume Desmottes + + * validate/data/gstvalidate.supp: + validate: be less specific when ignoring the pixman tls leak + Summary: I hit the same big in a slightly different code path. + Reviewers: thiblahute + Differential Revision: http://phabricator.freedesktop.org/D146 + +2015-05-04 14:22:00 +0200 Guillaume Desmottes + + * validate/.gitignore: + * validate/tools/.gitignore: + update gitignore + Reviewers: thiblahute + Differential Revision: http://phabricator.freedesktop.org/D145 + +2015-04-30 17:39:55 +0200 Guillaume Desmottes + + * validate/data/scenarios/setup_sink_props_max_lateness.scenario: + * validate/launcher/baseclasses.py: + use the setup_sink_props_max_lateness config scenario with valgrind + Reviewers: thiblahute + Differential Revision: http://phabricator.freedesktop.org/D141 + +2015-04-30 17:22:19 +0200 Guillaume Desmottes + + * validate/gst/validate/gst-validate-scenario.c: + validate: add 'target-element-klass' property on set-property action + Summary: + This allows us to set a property on all the elements of the pipeline matching + a specific klass name. + Reviewers: thiblahute + Differential Revision: http://phabricator.freedesktop.org/D140 + +2015-05-01 16:39:04 +0200 Guillaume Desmottes + + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + validate: add 'optional' action keyword + Reviewers: thiblahute + Differential Revision: http://phabricator.freedesktop.org/D139 + +2015-04-30 15:39:23 +0200 Guillaume Desmottes + + * validate/gst/validate/gst-validate-scenario.c: + validate: scenario: call _element_added_cb() on existing children + Summary: + We want to have a chance to set property on all the elements of the pipelines, + including the existing children when the element is added. + Reviewers: thiblahute + Differential Revision: http://phabricator.freedesktop.org/D138 + +2015-04-29 14:12:01 +0200 Guillaume Desmottes + + * validate/gst/validate/gst-validate-report.c: + * validate/launcher/httpserver.py: + * validate/launcher/vfb_server.py: + Fix 'stoped' typo + Reviewers: thiblahute + Differential Revision: http://phabricator.freedesktop.org/D137 + +2015-04-27 15:57:13 +0200 Guillaume Desmottes + + * validate/data/gstvalidate.supp: + validate: add vg suppression for libdrm bug + Reviewers: thiblahute + Differential Revision: http://phabricator.freedesktop.org/D134 + +2015-04-27 15:14:10 +0200 Guillaume Desmottes + + * validate/data/gstvalidate.supp: + validate: ignore invalid read from libav aac decoding + Reviewers: thiblahute + Differential Revision: http://phabricator.freedesktop.org/D133 + +2015-04-27 14:48:54 +0200 Guillaume Desmottes + + * validate/data/gstvalidate.supp: + validate: ignore libvpx valgrind errors + Reviewers: thiblahute + Differential Revision: http://phabricator.freedesktop.org/D132 + +2015-04-27 14:04:05 +0200 Guillaume Desmottes + + * validate/data/gstvalidate.supp: + * validate/launcher/apps/gstvalidate.py: + * validate/launcher/baseclasses.py: + validate: display the URL of ignored Valgrind bugs + Summary: + We don't want to forget about those so best to remind it when starting tests + as we do with blacklisted tests. + Reviewers: thiblahute + Differential Revision: http://phabricator.freedesktop.org/D131 + +2015-04-27 13:25:44 +0200 Guillaume Desmottes + + * validate/launcher/baseclasses.py: + * validate/launcher/utils.py: + validate: move look_for_file_in_source_dir and get_valgrind_suppression_file to utils + Reviewers: thiblahute + Differential Revision: http://phabricator.freedesktop.org/D130 + +2015-04-30 23:57:09 +0200 Thibault Saunier + + * validate/autogen.sh: + validate: Make sure to run submodule init from the root dir + +2015-04-29 13:22:11 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate:scenario: Fix 'duration' property of the pause action + We preparse it into and set it as GstClockTime in the + structures so make sure to use them as such. + +2015-04-28 16:44:42 +0200 Thibault Saunier + + * validate/launcher/baseclasses.py: + validate:launcher: Concider unset MediaDescriptor duration has 'infinite' + +2015-04-22 11:38:56 +0200 Guillaume Desmottes + + * validate/data/gstvalidate.supp: + validate: ignore a pixman leak which is fixed in master + http://phabricator.freedesktop.org/D128 + +2015-04-21 15:57:57 +0200 Guillaume Desmottes + + * validate/data/gstvalidate.supp: + validate: use a bigger hammer to ignore mesa related leaks + Looks like some tests are hitting a slightly different code path in udev but + the root bug is the same. + http://phabricator.freedesktop.org/D128 + +2015-04-23 12:33:26 +0100 Tim-Philipp Müller + + * codecanalyzer/src/codecanalyzer.c: + codecanalyzer: minor style fix + +2015-04-23 15:53:12 +0900 Vineeth T M + + * codecanalyzer/src/codecanalyzer.c: + codecanalyzer: don't try to free uninitialized pointers + xml_files_path and hex_files_path variable are not initialized. + There are chances that corruption happens when uninitialized + variables are freed, so init them to NULL before use. + https://bugzilla.gnome.org/show_bug.cgi?id=748351 + +2015-04-23 12:23:24 +0100 Tim-Philipp Müller + + * codecanalyzer/src/codecanalyzer.c: + codecanalyzer: run gst-indent on code + +2015-04-23 11:44:24 +0200 Thibault Saunier + + * validate/autogen.sh: + * validate/gst-validate.doap: + * validate/po/Makevars: + validate: Update autogen.sh + And add a gst-validate.doap file. + +2015-04-23 11:24:14 +0200 Thibault Saunier + + * validate/common: + Update common submodule + +2015-04-21 11:00:58 +0200 Guillaume Desmottes + + * validate/launcher/baseclasses.py: + * validate/tools/Makefile.am: + validate: use -debug versions of bins when running from source + Summary: + Those versions are using rpath instead of libtool's wrappers and so will be + faster to start and won't confuse valgrind. + Reviewers: thiblahute + Differential Revision: http://phabricator.freedesktop.org/D116 + +2015-04-20 15:24:46 +0200 Guillaume Desmottes + + * validate/configure.ac: + * validate/data/Makefile.am: + * validate/data/adaptive_video_framerate.scenario: + * validate/data/adaptive_video_framerate_size.scenario: + * validate/data/adaptive_video_size.scenario: + * validate/data/alternate_fast_backward_forward.scenario: + * validate/data/camerabin_signal.scenario: + * validate/data/change_state_intensive.scenario: + * validate/data/disable_subtitle_track_while_paused.scenario: + * validate/data/fast_backward.scenario: + * validate/data/fast_forward.scenario: + * validate/data/force_key_unit.scenario: + * validate/data/pause_resume.scenario: + * validate/data/play_15s.scenario: + * validate/data/reverse_playback.scenario: + * validate/data/scenarios/Makefile.am: + * validate/data/scenarios/adaptive_video_framerate.scenario: + * validate/data/scenarios/adaptive_video_framerate_size.scenario: + * validate/data/scenarios/adaptive_video_size.scenario: + * validate/data/scenarios/alternate_fast_backward_forward.scenario: + * validate/data/scenarios/camerabin_signal.scenario: + * validate/data/scenarios/change_state_intensive.scenario: + * validate/data/scenarios/disable_subtitle_track_while_paused.scenario: + * validate/data/scenarios/fast_backward.scenario: + * validate/data/scenarios/fast_forward.scenario: + * validate/data/scenarios/force_key_unit.scenario: + * validate/data/scenarios/pause_resume.scenario: + * validate/data/scenarios/play_15s.scenario: + * validate/data/scenarios/reverse_playback.scenario: + * validate/data/scenarios/scrub_backward_seeking.scenario: + * validate/data/scenarios/scrub_backward_seeking_full.scenario: + * validate/data/scenarios/scrub_forward_seeking.scenario: + * validate/data/scenarios/scrub_forward_seeking_full.scenario: + * validate/data/scenarios/seek_backward.scenario: + * validate/data/scenarios/seek_forward.scenario: + * validate/data/scenarios/seek_forward_backward.scenario: + * validate/data/scenarios/seek_with_stop.scenario: + * validate/data/scenarios/simple_seeks.scenario: + * validate/data/scenarios/switch_audio_track.scenario: + * validate/data/scenarios/switch_audio_track_while_paused.scenario: + * validate/data/scenarios/switch_set_external_subtitle.scenario: + * validate/data/scenarios/switch_subtitle_track.scenario: + * validate/data/scenarios/switch_subtitle_track_while_paused.scenario: + * validate/data/scenarios/update_start.scenario: + * validate/data/scenarios/update_stop.scenario: + * validate/data/scrub_backward_seeking.scenario: + * validate/data/scrub_backward_seeking_full.scenario: + * validate/data/scrub_forward_seeking.scenario: + * validate/data/scrub_forward_seeking_full.scenario: + * validate/data/seek_backward.scenario: + * validate/data/seek_forward.scenario: + * validate/data/seek_forward_backward.scenario: + * validate/data/seek_with_stop.scenario: + * validate/data/simple_seeks.scenario: + * validate/data/switch_audio_track.scenario: + * validate/data/switch_audio_track_while_paused.scenario: + * validate/data/switch_set_external_subtitle.scenario: + * validate/data/switch_subtitle_track.scenario: + * validate/data/switch_subtitle_track_while_paused.scenario: + * validate/data/update_start.scenario: + * validate/data/update_stop.scenario: + * validate/gst/validate/gst-validate-scenario.c: + move scenarios to data/scenarios + Differential Revision: http://phabricator.freedesktop.org/D115 + +2015-04-20 10:53:29 +0200 Guillaume Desmottes + + * validate/Makefile.am: + * validate/data/gstvalidate.supp: + * validate/launcher/baseclasses.py: + validate: add gstvalidate.supp valgrind suppression file + Differential Revision: http://phabricator.freedesktop.org/D115 + +2015-04-13 13:55:56 +0200 Guillaume Desmottes + + * validate/gst/validate/gst-validate-scenario.c: + validate: scenario: fix structure and action leak + +2015-04-10 11:28:34 +0200 Guillaume Desmottes + + * validate/gst/validate/gst-validate-report.c: + validate: report: fix GString leak when early returning + +2015-04-19 11:57:36 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-pipeline-monitor.c: + validate:pipelinemonitor: Print position only when in state >= PAUSED + Reviewers: Mathieu_Du + Reviewed By: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D86 + +2015-04-19 11:56:29 +0200 Thibault Saunier + + * validate/launcher/vfb_server.py: + validate:launcher: Use full HD screen as default screen size in xvfb + +2015-04-17 20:37:21 +0200 Thibault Saunier + + * validate/launcher/Makefile.am: + * validate/launcher/main.py: + * validate/launcher/vfb_server.py: + validate:launcher: Add a way to run tests without displaying the output + Summary: + Adding a --no-display option and running Xvfb virtual frame buffer X + server. + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D105 + +2015-04-17 19:56:17 +0200 Thibault Saunier + + * validate/launcher/main.py: + validate:launcher: Error out if valgrind is not available on the system + Summary: + When the user wants to use valgrind, make sure it is present on the + system before doing anything + Reviewers: gdesmott + Differential Revision: http://phabricator.freedesktop.org/D104 + +2015-04-17 19:28:19 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate:scenario: Stop scenario execution on stop action + And document it properly. + Summary: + The stop action was defined as "setting state to NULL" but + its actual goal is to stop the execution of the scenario. Make sure + that the scenario will not try to execute other actions when that + one has been executed. + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D103 + +2015-04-16 13:40:08 +0200 Thibault Saunier + + * validate/gst/validate/media-descriptor-writer.c: + * validate/gst/validate/media-descriptor.c: + validate: Gracefully handle absence of TAG on streams + Summary: And do not segfault when it happens! + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D99 + +2015-04-16 12:02:11 +0200 Thibault Saunier + + * validate/launcher/baseclasses.py: + validate:launcher: Pass GST_VALIDATE_SCENARIO to the subprocess env only + Summary: + And make sure to remove it from the env if the user has it in its main + environment. + Without that commit we ended up passing scenarios from previous tests + to the following ones where None were specified. + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D98 + +2015-03-31 15:10:11 +0200 Guillaume Desmottes + + * validate/gst/validate/media-descriptor-writer.c: + validate: don't pass NULL to gst_caps_copy() + +2015-03-31 14:54:28 +0200 Guillaume Desmottes + + * validate/gst/validate/media-descriptor-writer.c: + * validate/gst/validate/media-descriptor-writer.h: + * validate/tools/gst-validate-media-check.c: + validate: use GstMediaDescriptorWriter as log handler + Allow us to catch warnings when running gst-validate-media-check-1.0. + +2015-03-31 09:59:58 +0200 Guillaume Desmottes + + * validate/gst/validate/media-descriptor-writer.c: + validate: media-descriptor-writer: fix string leaks + +2015-04-15 14:02:32 +0900 Young Han Lee + + * validate/tools/gst-validate-launcher.in: + validate:launcher: Handle git error properly + 'OSError' exception is emitted but not handled properly when git is not + installed on running system. + https://bugzilla.gnome.org/show_bug.cgi?id=747892 + +2015-04-14 12:31:32 +0200 Guillaume Desmottes + + * validate/launcher/baseclasses.py: + validate: set GST_GL_XINITTHREADS + This ensure that XInitThreads is called and so gl contexts are properly + initialized. + https://bugzilla.gnome.org/show_bug.cgi?id=747840 + Signed-off-by: Guillaume Desmottes + +2015-04-10 18:19:40 +0200 Thibault Saunier + + * validate/launcher/main.py: + validate:launcher: Make validate the only default testsuite + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D93 + +2015-04-10 18:11:09 +0200 Thibault Saunier + + * validate/launcher/main.py: + validate:launcher: Let the responsibility to update asset to the testsuite + Summary: + It makes it easier to make sure that the assets needed for a specific + testsuite are available when needed + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D92 + +2015-04-10 13:29:47 +0200 Thibault Saunier + + * validate/tools/gst-validate-transcoding.c: + * validate/tools/gst-validate.c: + validate:tools: EOS handling is the responsibility of the scenario + Summary: If any scenario set + Reviewers: Mathieu_Du + Differential Revision: + http://phabricator.freedesktop.org/D90 + +2015-02-13 18:34:04 +0100 Ramiro Polla + + * validate/gst/validate/gst-validate-scenario.c: + validate:scenario: Add support for waiting on signals and messages + Reviewers: Mathieu_Du + Differential Revision: + http://phabricator.freedesktop.org/D88 + +2015-03-06 11:55:09 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + validate:scenario: Report disabling plugin issues + Summary: + + typedef GstValidateActionReturn so it can be used in the introspection + + Add GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED which should be used + to tell Validate that something wrong happened so the sub action + won't be executed, but that it should not report an error itself + as it has already been handled in the action function. + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D81 + +2015-03-06 11:51:19 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate:scenario: Allow execution of disable-plugin as a config action + Summary: + And fix a bug where config actions were added to the list of action even + if they had already been executed + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D80 + +2015-03-03 09:16:20 +0000 Thibault Saunier + + * validate/data/seek_forward_backward.scenario: + validate:scenarios: Set seek_forward_backward min-media-duration=45 + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D79 + +2015-02-27 23:20:43 +0000 Thibault Saunier + + * validate/launcher/apps/gstvalidate.py: + * validate/launcher/baseclasses.py: + validate:launcher: Set more env variable in the launcher command desc + Summary: + Adding if present: + * LD_PRELOAD + * DISPLAY + * GST_VALIDATE_CONFIG + * GST_VALIDATE_OVERRIDE + + enhance the add_env_variable method to more easily set envvar from + current value + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D78 + +2015-03-02 11:03:08 +0100 Thibault Saunier + + * validate/gst/plugins/gapplication/gstvalidategapplication.c: + * validate/gst/validate/validate.c: + * validate/gst/validate/validate.h: + validate: Add a method to easily get plugin configuration + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D77 + +2015-02-26 13:11:51 +0100 Thibault Saunier + + * validate/configure.ac: + * validate/gst/plugins/Makefile.am: + * validate/gst/plugins/gapplication/Makefile.am: + * validate/gst/plugins/gapplication/gstvalidategapplication.c: + validate:plugins: Add support to all GApplication as a test apps + Summary: + Add a very simple plugin that will allow any GApplication to easily be + used with GstValidate using the LD_PRELOAD feature + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D75 + +2015-01-17 22:21:16 +0100 Mathieu Duponchelle + + * validate/gst/validate/gst-validate-override-registry.c: + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-utils.c: + * validate/gst/validate/gst-validate-utils.h: + validate: let structs_from_filename be exported. + Summary: It is useful for plugins too + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D74 + +2015-03-03 15:42:06 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-internal.h: + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-scenario.c: + validate:scenario: Update Action.repeat field when needed + Summary: And print the current repeat value of the action that have such a field + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D73 + +2015-02-26 15:21:01 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate:scenario: Alway execute a 'quit' action on EOS + Summary: Making scenario more usable with LD_PRELOAD + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D72 + +2015-02-27 22:39:42 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate:scenario: Set the main action structure in fill_structure + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D71 + +2015-02-27 13:18:04 +0000 Thibault Saunier + + * validate/launcher/baseclasses.py: + validate:launcher: Use GST_VALIDATE_SCENARIO envvar to set scenarios + Summary: + Instead of concidering all apps will have a --set-scenario argument + which is not going to be the case as soon as we run the tests through + LD_PRELOAD + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D70 + +2015-02-27 13:16:01 +0000 Thibault Saunier + + * validate/launcher/apps/gstvalidate.py: + * validate/launcher/baseclasses.py: + validate:launcher: Move get_current_position from GstValidatePipelineTest to GstValidateTest + This is where it belongs + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D69 + +2015-04-08 14:13:11 +0900 Wonchul Lee + + * validate/docs/validate/scenarios.xml: + validate: fix typo in scenario file format docs + https://bugzilla.gnome.org/show_bug.cgi?id=747487 + +2015-03-30 16:47:28 +0200 Guillaume Desmottes + + * validate/gst/validate/gst-validate-scenario.c: + validate: scenario: fix caps leak + +2015-03-30 16:46:12 +0200 Guillaume Desmottes + + * validate/gst/validate/media-descriptor.c: + validate: media-descriptor: fix filenode->caps leak + +2015-03-27 16:00:50 +0100 Guillaume Desmottes + + * validate/tools/gst-validate-transcoding.c: + validate: transcoding: don't create a second mainloop + +2015-03-27 16:00:19 +0100 Guillaume Desmottes + + * validate/tools/gst-validate-transcoding.c: + validate: transcoding: don't leak the requested sinkpad from decodebin + +2015-03-27 15:59:42 +0100 Guillaume Desmottes + + * validate/gst/validate/gst-validate-pad-monitor.c: + validate: pad-monitor: fix caps leak + Don't create othercaps when early returning. + +2015-03-27 12:16:03 +0100 Guillaume Desmottes + + * validate/launcher/apps/gstvalidate.py: + validate: GstValidateMediaCheckTest should inherit from GstValidateTest + +2015-03-31 09:20:05 +0900 Young Han Lee + + * validate/launcher/Makefile.am: + validate:launcher: Install config.py for non-development mode + Running installed gst-validate-launcher aborted with the following error. + File "lib/gst-validate-launcher/python/launcher/baseclasses.py", line 28, in + import config + ImportError: No module named config + This is because config.py is added but not installed + in ba6d209b3fd062f4e6bd889f81f1213cc12339ec. + https://bugzilla.gnome.org/show_bug.cgi?id=747087 + +2015-03-29 11:13:01 +0900 Young Han Lee + + * validate/launcher/baseclasses.py: + validate:launcher: Show timeout seconds for timeout result message + Current timeout message doesn't show how many seconds a test took and + it is timeouted by normal timeout or hard timeout. + This patch changes the message like following. + 1. normal timeout + old : validate.http.playback.reverse_playback.raw_video_mov: Timeout (Application timed out) + new : validate.http.playback.reverse_playback.raw_video_mov: Timeout (Application timed out: 120 secs) + 2. hard timeout + old : validate.http.playback.reverse_playback.raw_video_mov: Timeout (Application timed out) + new : validate.http.playback.reverse_playback.raw_video_mov: Timeout (Hard timeout reached: 600 secs) + https://bugzilla.gnome.org/show_bug.cgi?id=746957 + +2015-03-30 16:00:09 +0900 Young Han Lee + + * validate/launcher/baseclasses.py: + validate:launcher: Fix wrong test number with -j option + When '-j n' option is given, first n tests print test number 0. + This is caused by test_num part of 919db986052602dca452f05e284cfc857302d4f0. + https://bugzilla.gnome.org/show_bug.cgi?id=747006 + +2015-03-28 23:29:56 +0100 Thibault Saunier + + * validate/configure.ac: + * validate/launcher/baseclasses.py: + * validate/launcher/config.py.in: + validate:launcher: Avoid depending on PyGObject + Summary: + And rely on our knowledge of the configuration to figure out where the + suppression file has been installed + Reviewers: gdesmott + Differential Revision: http://phabricator.freedesktop.org/D61 + +2015-03-26 15:42:11 +0100 Guillaume Desmottes + + * validate/gst/validate/media-descriptor-writer.c: + validate: media-descriptor-writer: don't leak info and streaminfo + +2015-03-26 15:39:12 +0100 Guillaume Desmottes + + * validate/tools/gst-validate-media-check.c: + validate: media-check: don't leak output_file and expected_file + +2015-03-26 13:59:30 +0100 Guillaume Desmottes + + * validate/launcher/baseclasses.py: + validate: check VALGRIND_ERROR_CODE in Test as well + We were doing it only in GstValidateTest which was overriding the default + implementation. + +2015-03-26 13:57:34 +0100 Guillaume Desmottes + + * validate/launcher/baseclasses.py: + validate: don't increase hard_timeout is if it's None + Some tests, like the media check ones, have None as hard_timeout. + +2015-03-26 11:29:26 +0100 Guillaume Desmottes + + * validate/launcher/baseclasses.py: + validate: increase VALGRIND_TIMEOUT_FACTOR + 5 wasn't enough for my poor laptop. + +2015-03-26 11:29:06 +0100 Guillaume Desmottes + + * validate/launcher/baseclasses.py: + validate: increase the normal timeout as well when using valgrind + +2015-03-26 10:32:09 +0100 Guillaume Desmottes + + * validate/gst/validate/media-descriptor.c: + validate: media-descriptor: fix caps leak + gst_pad_get_current_caps() returns a reffed caps. + +2015-03-23 13:36:45 +0100 Guillaume Desmottes + + * validate/tools/gst-validate-media-check.c: + * validate/tools/gst-validate-transcoding.c: + also call gst_deinit() in media-check and transcoding + More valgrind friendly. + +2015-03-23 16:19:49 +0100 Guillaume Desmottes + + * validate/launcher/baseclasses.py: + validate: raise an error if valgrind detected issues + Differential Revision: http://phabricator.freedesktop.org/D53 + +2015-03-23 13:36:45 +0100 Guillaume Desmottes + + * validate/tools/gst-validate.c: + call gst_deinit() when we are done + More valgrind friendly. + +2015-03-23 13:35:41 +0100 Guillaume Desmottes + + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/media-descriptor-writer.c: + * validate/tools/gst-validate-transcoding.c: + * validate/tools/gst-validate.c: + validate: call gst_bus_remove_signal_watch() + We are supposed to call gst_bus_remove_signal_watch() for each gst_bus_add_signal_watch() call to prevent leaks. + +2015-03-23 10:24:21 +0100 Guillaume Desmottes + + * validate/gst/validate/media-descriptor-parser.c: + validate: media-descriptor-parser: fix string leak + _set_content() doesn't actually consume @content so the caller is responsible + freeing it. + +2015-03-23 10:23:02 +0100 Guillaume Desmottes + + * validate/gst/validate/gst-validate-pad-monitor.c: + validate: pad-monitor: fix buffers list leak + +2015-03-23 10:22:47 +0100 Guillaume Desmottes + + * validate/gst/validate/gst-validate-pad-monitor.c: + validate: pad-monitor: fix caps leak + +2015-03-20 15:22:32 +0100 Guillaume Desmottes + + * validate/gst/validate/gst-validate-media-info.c: + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/media-descriptor-writer.c: + validate: fix a bunch of GstBus leaks + +2015-03-23 09:39:30 +0100 Guillaume Desmottes + + * validate/launcher/baseclasses.py: + validate: store valgrind logs to its own file + +2015-03-20 15:00:28 +0100 Guillaume Desmottes + + * validate/gst/validate/gst-validate-scenario.c: + validate: report: fix invalid read when destroying Report + Summary: + @report was invalid when we were trying to clear the mutex. + validate: scenario: remove weak pointer when destroying action + Free an invalid read when the scenario is destroyed after the action. + Differential Revision: http://phabricator.freedesktop.org/D44 + +2015-03-20 14:49:24 +0100 Guillaume Desmottes + + * validate/gst/validate/gst-validate-report.c: + validate: report: fix invalid read when destroying Report + @report was invalid when we were trying to clear the mutex. + +2015-03-20 12:15:03 +0100 Guillaume Desmottes + + * validate/tools/gst-validate.c: + validate: fix string arguments leaks + We are responsible of freeing the string arguments parsed by GOptionContext. + +2015-03-20 11:39:32 +0100 Guillaume Desmottes + + * validate/gst/validate/gst-validate-scenario.c: + validate: scenario: don't borrow @structure in _fill_action() + @structure was borrowed in some code path and wasn't in some other. Make it + clearer, and fix a leak, by always copying it. + +2015-03-20 11:33:01 +0100 Guillaume Desmottes + + * validate/gst/validate/gst-validate-override-registry.c: + * validate/gst/validate/gst-validate-utils.c: + validate: override-registry: fix structs list leak + The list returned by _lines_get_strutures() needs to be deeply freed. + +2015-03-20 11:27:29 +0100 Guillaume Desmottes + + * validate/gst/validate/gst-validate-scenario.c: + validate: scenario: fix scenarios leak + +2015-03-20 11:25:39 +0100 Guillaume Desmottes + + * validate/gst/validate/gst-validate-reporter.c: + validate: reporter: fix message leak + +2015-03-20 11:24:27 +0100 Guillaume Desmottes + + * validate/gst/validate/gst-validate-report.c: + validate: report: don't shadow the GString variable + We were leaking the GString as it's freed outside of the block. + +2015-03-20 11:24:04 +0100 Guillaume Desmottes + + * validate/gst/validate/gst-validate-report.c: + validate: report: fix GStrv leak + +2015-03-20 11:23:29 +0100 Guillaume Desmottes + + * validate/gst/validate/gst-validate-report.c: + validate: report: fix GStrv leak + We borrow the content of the GStrv but were leaking the array itself. + +2015-03-19 17:22:26 +0100 Guillaume Desmottes + + * validate/launcher/baseclasses.py: + launcher: try using gst.supp as valgrind suppressions file + https://bugzilla.gnome.org/show_bug.cgi?id=746465 + +2015-03-19 17:44:19 +0100 Guillaume Desmottes + + * validate/Makefile.am: + validate: install gst.supp + Will be used when running tests inside Valgrind. + https://bugzilla.gnome.org/show_bug.cgi?id=746465 + +2015-03-19 16:06:54 +0100 Guillaume Desmottes + + * validate/launcher/baseclasses.py: + * validate/launcher/main.py: + launcher: add valgrind support + Add a --valgrind option to gst-validate-launcher to run the tests inside + Valgrind and tune GLib's memory allocator accordingly. + Fix https://bugzilla.gnome.org/show_bug.cgi?id=746465 + +2015-03-20 10:06:35 +0100 Guillaume Desmottes + + * validate/data/Makefile.am: + * validate/docs/validate/envvariables.xml: + * validate/docs/validate/scenarios.xml: + * validate/gst/validate/gst-validate-scenario.c: + validate: move scenarios to validate/scenarios/ + https://bugzilla.gnome.org/show_bug.cgi?id=746465 + +2015-03-19 12:22:39 +0100 Guillaume Desmottes + + * validate/launcher/main.py: + validate:launcher: Fix small typo + +2015-03-18 17:05:19 +0100 Thibault Saunier + + * validate/launcher/main.py: + validate:launcher: Make sure to show apps specific options in the help + +2015-03-18 11:05:08 +0100 Thibault Saunier + + * validate/launcher/baseclasses.py: + validate:launcher: Fix test number printing + +2015-03-14 15:40:17 +0000 Thibault Saunier + + * validate/launcher/baseclasses.py: + validate:launcher Rename _other_testsuite_for_tester + To _check_tester_has_other_testsuite + +2015-03-14 15:08:12 +0000 Thibault Saunier + + * validate/launcher/baseclasses.py: + validate:launcher: Cache all the tests in the runner + This way we do not have to re ask all the test managers + what tests should be run. + +2015-03-13 17:09:08 +0000 Thibault Saunier + + * validate/launcher/apps/gstvalidate.py: + * validate/launcher/baseclasses.py: + validate:launcher: Add a way to simply run validate default tests on uris + Summary: + This allows us to easily run all the scenarios on a particular file doing: + $ gst-validate-launcher validate --validate-check-uri file:///some/media/file.webm + Reviewers: Mathieu_Du + Differential Revision: http://phabricator.freedesktop.org/D36 + +2015-03-13 17:07:00 +0000 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate: keep executing actions even after linking up following execution + When linking actions execution without waiting on execution context, then + idle callback should keep being called so following action keep being + executed. + +2015-03-10 10:29:28 +0100 Thibault Saunier + + * .arcconfig: + * validate/.gitignore: + validate: Add more files to .gitignore + Differential Revision: http://phabricator.freedesktop.org/D34 + +2015-03-10 10:25:23 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate:scenario: Rename get_position to execute_next_action + That function was wrongly called and did not correspond to what it + actually does. + +2015-03-09 18:26:37 +0000 Vincent Penquerc'h + + * validate/data/Makefile.am: + * validate/data/seek_backward_non_flushing.scenario: + * validate/data/seek_forward_non_flushing.scenario: + * validate/gst/validate/gst-validate-scenario.c: + Revert "validate: add non flushing seek support" + This reverts commit 3ff55dcc3119b39e7c86044159db8bce49a2dc3a. + Regressions on the test server, apparently linked to this patchset. + +2015-03-09 18:26:33 +0000 Vincent Penquerc'h + + * validate/gst/validate/gst-validate-scenario.c: + Revert "validate: use segments to detect success of flushing seeks too" + This reverts commit c47cc7ba90e96ffaefe201087428ef448670f3be. + Regressions on the test server, apparently linked to this patchset. + +2015-03-09 18:26:06 +0000 Vincent Penquerc'h + + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-report.h: + * validate/gst/validate/gst-validate-scenario.c: + Revert "validate: expect a buffer with discontinuity after a seek" + This reverts commit 87064b6994e36203b6976d436feda809068f1497. + Regressions on the test server, apparently linked to this patchset. + +2015-03-09 18:41:54 +0100 Thibault Saunier + + * validate/launcher/baseclasses.py: + validate:launcher: Make sure TIMEOUTs do not get converted to ERROR + This was a regression introduced in c0e3d2e4f190fc9627897cc3d3d016448cb5dbe9 + +2015-02-27 16:56:06 +0000 Vincent Penquerc'h + + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-report.h: + * validate/gst/validate/gst-validate-scenario.c: + validate: expect a buffer with discontinuity after a seek + https://bugzilla.gnome.org/show_bug.cgi?id=744783 + +2015-02-27 14:40:09 +0000 Vincent Penquerc'h + + * validate/gst/validate/gst-validate-scenario.c: + validate: use segments to detect success of flushing seeks too + https://bugzilla.gnome.org/show_bug.cgi?id=744783 + +2015-02-19 13:12:50 +0000 Vincent Penquerc'h + + * validate/data/Makefile.am: + * validate/data/seek_backward_non_flushing.scenario: + * validate/data/seek_forward_non_flushing.scenario: + * validate/gst/validate/gst-validate-scenario.c: + validate: add non flushing seek support + and a couple scenarios using them + https://bugzilla.gnome.org/show_bug.cgi?id=744783 + +2015-03-06 09:39:10 +0100 Thibault Saunier + + * validate/launcher/apps/Makefile.am: + validate:launcher: Do not forget to install apps/__init__.py + It is a python module that should be usable by external apps/testsuites + +2015-03-05 13:33:27 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate:scenario: Handle not mandatory action types + Summary: + There is currently no way to handle the fact that action types + might be handled only by a specific application but not handling + this action types would not cause any difference for the good execution + of the scenario as a whole + Differential Revision: http://phabricator.freedesktop.org/D33 + +2015-03-02 17:32:56 +0100 Thibault Saunier + + * validate/launcher/baseclasses.py: + validate:launcher: Better handle GST debug log outputs redirection + +2015-03-04 17:30:41 +0100 Thibault Saunier + + * validate/launcher/baseclasses.py: + validate:launcher: First rely on the presence of criticals to set tests result + In the case of external applications they might not set their exist + code bases on the result of validate so we should rely on what + validates as to say first. + +2015-03-04 17:26:55 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-report.h: + * validate/gst/validate/gst-validate-reporter.c: + validate:report: Allow registering of issue types through the introspection + Fixing annotations and make GstValidateIssue refcounted + We break the ABI in that commit but I do not expect anyone to register + issue type outside GstValidate yet. + Add padding in the structures so we can avoid breaking the ABI again later. + +2015-03-04 17:24:52 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-monitor.c: + validate:monitor: Do not requiere a GstObject as target + We can work with any GObject and that allows applications to write + monitors for other aspects too + +2015-03-03 12:26:52 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate:scenario: Alway execute sub action on action running SYNC + Move methods around to avoid needing on top prototypes + +2015-03-03 19:26:33 +0900 Wonchul Lee + + * validate/docs/validate/command-line-tools.xml: + validate:docs: Rename gst-validate-launch to gst-validate-launcher + https://bugzilla.gnome.org/show_bug.cgi?id=745510 + +2015-03-03 11:33:06 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-scenario.c: + validate:scenario: Do not execute last sub action twice when ASYNC + +2015-03-03 10:39:52 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate:scenario: Properly print sub action as if they were main actions + +2015-03-02 14:38:16 +0100 Emanuele Aina + + * validate/docs/validate/scenarios.xml: + validate:docs: Fix typos in Scenario File Format + https://bugzilla.gnome.org/show_bug.cgi?id=736160 + +2015-02-26 18:51:57 +0100 Thibault Saunier + + * validate/tools/gst-validate-launcher.in: + validate:launcher Do not use git -C as it is relatively recent + https://bugzilla.gnome.org/show_bug.cgi?id=736160 + +2015-02-26 11:09:23 +0100 Thibault Saunier + + * validate/gst/preload/Makefile.am: + validate: Build the preload so when possible + +2015-02-24 19:32:37 +0100 Thibault Saunier + + * validate/launcher/main.py: + validate:launcher: Print the long help in less when possible + +2015-02-24 19:08:12 +0100 Thibault Saunier + + * validate/launcher/main.py: + validate:launcher: Mention testsuite implementation in the help + +2015-02-23 12:24:39 +0100 Thibault Saunier + + * validate/configure.ac: + validate: Define GST_PLUGIN_LDFLAGS as needed + +2015-02-19 20:53:16 +0900 Young Han Lee + + * validate/tools/gst-validate-launcher.in: + validate: Determine development mode using git hash value + Development mode has been determined by whether the launcher is in git + repo + or not. This could be wrong when the launcher is installed to + subdirectory of other project's git repo, such as jhbuild. It is normal + to install compiled output to subdirectory of your jhbuild. + Changed logic gets the first commit hash of current git repo and + compares it with gst-devtools' the first commit hash. + https://bugzilla.gnome.org/show_bug.cgi?id=744781 + +2015-02-19 11:32:05 +0100 Thibault Saunier + + * validate/launcher/apps/gstvalidate.py: + * validate/launcher/baseclasses.py: + validate:launcher: Fix typo s/FILE_EXTENDION/FILE_EXTENSION/g + +2015-02-18 14:23:16 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-internal.h: + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + validate:scenario: Fix GstValidateAction ABI adding a private structure + This way we can easily extend the structure and avoid needing using + a union and such + +2015-01-22 22:29:10 +0100 Mathieu Duponchelle + + * validate/tests/check/validate/padmonitor.c: + validate: Test buffer outside of received range. + Summary: As part of the preparation for a port to tracer. + Test Plan: This is a test, we won't test tests + Reviewers: tsaunier + Differential Revision: http://internal.opencreed.com:8888/D19 + +2015-02-18 11:36:59 +0000 Tim-Philipp Müller + + * codecanalyzer/src/gst_analyzer.c: + codecanalyzer: fix codec detection with git master + The names might be 'MPEG-2 (Simple Profile)' now. + Shouldn't really rely on codec name strings here + in the first place, but use caps instead. + +2015-02-18 10:05:55 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-pipeline-monitor.c: + validate: Properly notify user about missing plugins + This way it is clear in gst-validate-launcher that the failure is due + to a missing plugin + +2015-02-17 18:18:56 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.h: + validate: Fix wrong sizeof usage + sizeof(int) is always <= sizeof(gpointer) + +2015-01-21 13:13:02 +0100 Ramiro Polla + + * validate/launcher/baseclasses.py: + validate: launcher: Use cElementTree for XML parsing + Using cElementTree instead of ElementTree speeds up parsing of media + descriptor files. + The total time spent parsing XML files drops from ~0.64 s to ~0.24 s, + leading to faster initialisation times for gst-validate-launcher. + https://bugzilla.gnome.org/show_bug.cgi?id=743293 + +2015-02-17 14:56:47 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + validate: Print actions directly from the scenario + Avoiding user to have to print them in each and every action type + implementation. + This requires adding some API to prepare actions before printing them. + Preparing action in that case mean parsing the values contained in the + GstStructure parsing equations and setting back the actual value + afterward + API: + * GstValidatePrepateAction + * gst_validate_action_type_set_prepare_function + +2015-02-16 22:12:54 +0100 Thibault Saunier + + * validate/gst/validate/validate.c: + validate: Fix build on windows + Check where libgstvalidate.dll is installed and use that base folder to + figure out where GstValidate plugins are installed + +2015-02-16 20:52:54 +0100 Thibault Saunier + + * validate/data/Makefile.am: + * validate/gst/validate/gst-validate-types.h: + validate: Do not forget to dist _full variant of scrubing scenarios + +2015-02-16 19:49:50 +0100 Thibault Saunier + + * validate/gst/validate/Makefile.am: + * validate/gst/validate/gst-validate-report.h: + * validate/gst/validate/gst-validate-scenario.h: + * validate/gst/validate/gst-validate-types.h: + * validate/gst/validate/validate.h: + validate: Create a gst-validate-types.h header where we define types + And include it from validate.h. + This way we avoid to need to typedef GstValidateAction twice, which is + a C11 feature + +2015-02-16 19:24:23 +0100 Edward Hervey + + * validate/gst/validate/gst-validate-report.c: + validate-report: Fix valist usage + a va_list always 'exists' (it's a struct). It therefore can't be NULL + (and can't be tested) + Just use the regular print variant where appropriate. + +2015-02-16 16:47:37 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate:scenario: Document locking + +2015-02-13 12:17:37 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate:scenario: Make get_position happen on idle + Summary: + - Add a way to force action to be executed in their own GSource dispatch, disabling chain action execution + API: + GstValidateScenario::execute-on-idle property + +2015-02-12 16:23:49 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + validate:scenario: Add a method to retrieve all remaining actions + Not only the next one as it was not making much sense! + API: + - gst_validate_scenario_get_next_action + + gst_validate_scenario_get_actions + +2015-02-12 16:13:09 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-utils.c: + validate:utils: Fix some annotations + +2015-02-12 16:10:00 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + validate:scenario: Allow link up of action executions for overriden types + Exposing a GstValidateActionType.overriden_type field + And properly expose gst_validate_execute_action + +2015-02-12 16:09:11 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-reporter.c: + validate:reporter: Always print reports in the Gst debug system + +2015-02-11 18:27:10 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate: Misc fixes + +2015-02-11 17:06:06 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate: Properly annotate gst_validate_register_action_type* + It does not return any reference to the type + +2015-02-10 13:50:23 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + Revert "validate:scenario: Add a way to specify action structure size" + This reverts commit b976319ef7f977b8ce910c4b8aa1a843da3b264f. + Now that the exact same structure can be used to represent different + action types, we can not rely on the structure size to stuff + informations into the action. Users should just make use of + GstMiniObject.qdata. + +2015-02-10 13:39:43 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate:scenario: We do not own any ref in GstValidateExecuteAction + And gst_validate_action_set_done might very well unref the last + reference to the action + +2015-02-10 13:22:34 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate: Make sure that the latest action type registration is kept + Avoiding to change the behaviour! + +2015-02-07 12:51:30 +0100 Thibault Saunier + + * validate/data/change_state_intensive.scenario: + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + validate:scenario: Handle scenario repeat property with sub actions + And port change_state_intensive.scenario to it + +2015-02-07 11:19:22 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-report.h: + * validate/gst/validate/gst-validate-scenario.c: + validate:scenario: Add the notion of sub actions + Sub action will allow user to executed action *right* after the + previous action has been completed, meaning in the end that both + action can be considered as one single action. + + Factor out a function to fill an GstValidateAction structure from a + GstStructure + + Factor out a function to set action playback time + +2015-02-06 12:20:30 +0100 Thibault Saunier + + * validate/docs/validate/envvariables.xml: + validate: Document some env variable usage + +2015-02-06 11:46:13 +0100 Thibault Saunier + + * validate/gst/plugins/fault_injection/socket_interposer.c: + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + validate: Add an API to cleanly register action type from plugins + API: + gst_validate_register_action_type_dynamic + https://bugzilla.gnome.org/show_bug.cgi?id=743994 + +2015-02-04 22:12:48 +0100 Thibault Saunier + + * validate/gst/plugins/fault_injection/Makefile.am: + * validate/gst/plugins/fault_injection/socket_interposer.c: + validate: Rename libfaultinjector to libgstvalidatefaultinjector + https://bugzilla.gnome.org/show_bug.cgi?id=743994 + +2015-02-04 15:14:04 +0100 Thibault Saunier + + * validate/gst/plugins/fault_injection/socket_interposer.c: + * validate/gst/validate/gst-validate-scenario.c: + validate: Use plugin name as implementer_namespace when registering action type + And document it as a good practice as it will allow us to map plugins + and action types + https://bugzilla.gnome.org/show_bug.cgi?id=743994 + +2015-02-04 14:54:55 +0100 Thibault Saunier + + * validate/Makefile.am: + * validate/configure.ac: + * validate/fault_injection/Makefile.am: + * validate/fault_injection/socket_interposer.c: + * validate/fault_injection/socket_interposer.h: + * validate/gst/Makefile.am: + * validate/gst/plugins/Makefile.am: + * validate/gst/plugins/fault_injection/Makefile.am: + * validate/gst/plugins/fault_injection/socket_interposer.c: + validate: Move the fault_injection plugin to gst/plugins/ + https://bugzilla.gnome.org/show_bug.cgi?id=743994 + +2015-02-04 14:50:14 +0100 Thibault Saunier + + * validate/gst/validate/validate.c: + validate: Use an actual GstRegistry to track our plugins + Keeping everything internal for now + https://bugzilla.gnome.org/show_bug.cgi?id=743994 + +2015-02-04 14:30:05 +0100 Thibault Saunier + + * validate/configure.ac: + * validate/fault_injection/Makefile.am: + * validate/fault_injection/socket_interposer.c: + * validate/fault_injection/socket_interposer.h: + * validate/gst/validate/Makefile.am: + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/validate.c: + validate: Implement fault_injection as a Gs(tValidate)Plugin + https://bugzilla.gnome.org/show_bug.cgi?id=743994 + +2015-02-02 18:00:14 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + validate:scenario: Add a way to specify action structure size + And return the register GstValidateActionType on registration + https://bugzilla.gnome.org/show_bug.cgi?id=743994 + +2015-02-02 11:41:24 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + scenario: Do not concider we are seek_in_paused if executing a new action + The new action might change the position on purpose and we should not + fail in that case. + Also at that point we know the test of position after the seek has + been executed + + Minor cosmetic fixes + https://bugzilla.gnome.org/show_bug.cgi?id=743994 + +2015-01-20 09:59:23 +0100 Thibault Saunier + + * validate/launcher/baseclasses.py: + validate:launcher:baseclasses: Avoid raising axception when all getting scenarios + https://bugzilla.gnome.org/show_bug.cgi?id=743994 + +2015-01-13 19:07:04 +0100 Thibault Saunier + + * validate/launcher/main.py: + launcher: Use gst-integration-testsuites FDO git repo + And make sure that people that were using the old repo get the origin + repo properly updated. + https://bugzilla.gnome.org/show_bug.cgi?id=743994 + +2015-02-04 15:27:37 +0100 Thibault Saunier + + * validate/launcher/apps/gstvalidate.py: + * validate/launcher/baseclasses.py: + validate: launcher: Allow discovering scenario from full path + https://bugzilla.gnome.org/show_bug.cgi?id=743994 + +2015-02-04 15:25:50 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + validate: scenario: Add a method to get the following action to be executed + API: + + gst_validate_scenario_get_next_action + https://bugzilla.gnome.org/show_bug.cgi?id=743994 + +2015-02-04 15:24:35 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-report.h: + * validate/gst/validate/gst-validate-reporter.c: + * validate/gst/validate/gst-validate-reporter.h: + * validate/gst/validate/gst-validate-runner.c: + validate: Add helper functions ti print actions + API: + + gst_validate_scenario_get_next_action + + gst_validate_reporter_report_simple + https://bugzilla.gnome.org/show_bug.cgi?id=743994 + +2015-02-04 15:23:29 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.h: + validate: Minor documentation fixes + https://bugzilla.gnome.org/show_bug.cgi?id=743994 + +2015-02-04 15:18:22 +0100 Thibault Saunier + + * validate/data/seek_with_stop.scenario: + validate: Set seek_with_stop as needing at least 2secs media files + https://bugzilla.gnome.org/show_bug.cgi?id=743994 + +2014-12-13 23:23:11 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + * validate/launcher/apps/gstvalidate.py: + validate: Fix the check of action that can be *not* executed + The check was wrong and we ended up allowing seek actions to no be + executed. + API: + GST_VALIDATE_ACTION_TYPE_NO_EXECUTION_NOT_FATAL + https://bugzilla.gnome.org/show_bug.cgi?id=743994 + +2014-12-13 23:16:27 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + validate:scenario; Advertise action types that will be executed on addition + Adding a flag to the action type + And make that code thread safe. + https://bugzilla.gnome.org/show_bug.cgi?id=743994 + +2014-12-13 23:12:30 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + validate:scenario: Add a Flag fore ActionType that need clocks sync + And cleanly use it to set the need-clock-sync field in + the scenario properties + https://bugzilla.gnome.org/show_bug.cgi?id=743994 + +2014-12-13 19:17:45 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + validate: Add the notion of INTERLACED actions + An interlaced action is an action that will be executed ASYNC but + without that will not block following actions during its execution. + The action should be set to done later on at any point during the + execution of the scenario. + API: + + GST_VALIDATE_EXECUTE_ACTION_INTERLACED + + GST_VALIDATE_ACTION_TYPE_INTERLACED + https://bugzilla.gnome.org/show_bug.cgi?id=743994 + +2014-12-13 19:15:59 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-internal.h: + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + validate: Add a way to retrieve register actoin type from outside + API: + * GstValidateActionType + * gst_validate_get_action_type + https://bugzilla.gnome.org/show_bug.cgi?id=743994 + +2014-12-13 16:01:49 +0100 Thibault Saunier + + * validate/launcher/apps/gstvalidate.py: + * validate/launcher/baseclasses.py: + validate:launcher: Implement a FakeMediaDescriptor + This allows us to more cleanly implement Simple pipeline test + generation + https://bugzilla.gnome.org/show_bug.cgi?id=743994 + +2014-12-13 16:00:19 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.h: + validate:scenario: Make action->scenario public API + It can be usefull for action type implementers + https://bugzilla.gnome.org/show_bug.cgi?id=743994 + +2014-12-13 16:00:12 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate:scenario: Add a disable-plugin action type + https://bugzilla.gnome.org/show_bug.cgi?id=743994 + +2014-12-12 14:41:38 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate: Properly advertise the wait action as ASYNC + And add some printing when executing the set-property action + https://bugzilla.gnome.org/show_bug.cgi?id=743994 + +2014-12-12 14:36:16 +0100 Thibault Saunier + + * validate/launcher/apps/gstvalidate.py: + validate: Enhance support for simple pipeline test generation + The GstValidatePipelineGenerator was quite limited in term + of configuration for user who just want to specify pipelines + to run with/without scenario. + Enhance the API so that we can properly configure that. + https://bugzilla.gnome.org/show_bug.cgi?id=743994 + +2014-12-11 14:21:12 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + * validate/tools/gst-validate.c: + validate: Wait for switch-track to complete before executing next action + This action type can take some time, we need to make sure that the + combiner/input-selector element properly pushed a buffer marked + as DISCONT to concider the action is done. + https://bugzilla.gnome.org/show_bug.cgi?id=743994 + +2014-12-11 12:08:13 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + validate:scenario: Avoid waiting for 50ms between actions + We should be able to execute the next action as soon as the previous + one is fully completed, make sure the code tries to do that and does + not artificially add some waiting time. + And make sure if the gst_validate_action_set_done is called from outside + our execution thread, we do not try to execute anything + https://bugzilla.gnome.org/show_bug.cgi?id=743994 + +2014-12-10 20:37:58 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + scenario: Add a signal to notify user when the scenario is DONE executing + https://bugzilla.gnome.org/show_bug.cgi?id=743994 + +2015-01-20 16:44:07 +0100 Ramiro Polla + + * validate/launcher/baseclasses.py: + validate: launcher: Print test name in Result + https://bugzilla.gnome.org/show_bug.cgi?id=743063 + +2015-01-16 21:29:55 +0100 Ramiro Polla + + * validate/launcher/baseclasses.py: + * validate/launcher/main.py: + validate: launcher: Add option to run tests in parallel + Patch 4/4 to implement parallel test execution. + https://bugzilla.gnome.org/show_bug.cgi?id=743063 + +2015-01-16 21:09:37 +0100 Ramiro Polla + + * validate/launcher/baseclasses.py: + validate: launcher: Print test number on result + With parallel test execution, it will be hard to track which result + relates to which test. Therefore, the test number should be printed + along with the results as well. + Patch 3/4 to implement parallel test execution. + https://bugzilla.gnome.org/show_bug.cgi?id=743063 + +2015-01-16 21:08:54 +0100 Ramiro Polla + + * validate/launcher/baseclasses.py: + validate: launcher: Use jobs list to take track of tests running + Currently the tests are still run serially. + Patch 2/4 to implement parallel test execution. + https://bugzilla.gnome.org/show_bug.cgi?id=743063 + +2015-01-16 20:35:33 +0100 Ramiro Polla + + * validate/launcher/baseclasses.py: + validate: launcher: Use test index instead of counting test numbers + Patch 1/4 to implement parallel test execution. + https://bugzilla.gnome.org/show_bug.cgi?id=743063 + +2015-01-19 10:35:03 +0100 Ramiro Polla + + * validate/launcher/RangeHTTPServer.py: + validate: launcher: Support simultaneous requests in RangeHTTPServer + https://bugzilla.gnome.org/show_bug.cgi?id=743063 + +2015-01-16 19:08:19 +0100 Ramiro Polla + + * validate/launcher/baseclasses.py: + validate: launcher: Make TestManager handle waiting for processes + Patch 4/4 to make TestManager handle waiting for processes instead of + expecting each Test to do it. + https://bugzilla.gnome.org/show_bug.cgi?id=743063 + +2015-01-16 19:03:07 +0100 Ramiro Polla + + * validate/launcher/baseclasses.py: + validate: launcher: Use a Queue to test for test completion + TestManager will use a Queue to track progress for all tests. This + commit implements a queue inside Test to simplify the transition. + Patch 3/4 to make TestManager handle waiting for processes instead of + expecting each Test to do it. + https://bugzilla.gnome.org/show_bug.cgi?id=743063 + +2015-01-16 19:00:25 +0100 Ramiro Polla + + * validate/launcher/baseclasses.py: + validate: launcher: Initialize Test start time outside of wait_process + wait_process will be moved to TestManager, so the values used to track + process update must remain inside Test. + Patch 2/4 to make TestManager handle waiting for processes instead of + expecting each Test to do it. + +2015-01-16 18:57:06 +0100 Ramiro Polla + + * validate/launcher/baseclasses.py: + validate: launcher: Split process_update() out of wait_process() + Patch 1/4 to make TestManager handle waiting for processes instead of + expecting each Test to do it. + +2015-01-16 18:50:38 +0100 Ramiro Polla + + * validate/launcher/baseclasses.py: + * validate/launcher/reporters.py: + validate: launcher: Move logfile handling out of Reporter and into Test + This makes each Test handle its own logfile, allowing the Reporter to + work on multiple tests at the same time. + Patch 5/5 to move logfile handling out of Reporter and into Test. + +2015-01-16 19:54:56 +0100 Ramiro Polla + + * validate/launcher/reporters.py: + validate: launcher: Remove redundant check + self.out is always available when _get_captured() is called. + Patch 4/5 to move logfile handling out of Reporter and into Test. + +2015-01-16 18:45:52 +0100 Ramiro Polla + + * validate/launcher/baseclasses.py: + * validate/launcher/reporters.py: + validate: launcher: Split test log file handling in Reporter + Patch 3/5 to move logfile handling out of Reporter and into Test. + +2015-01-16 18:42:19 +0100 Ramiro Polla + + * validate/launcher/baseclasses.py: + * validate/launcher/reporters.py: + validate: launcher: Separate Reporter from current Test + Instead of saving the current Test in Reporter for every test, use + function parameters to achieve the same goal. + Patch 2/5 to move logfile handling out of Reporter and into Test. + +2015-01-16 18:25:56 +0100 Ramiro Polla + + * validate/launcher/baseclasses.py: + * validate/launcher/reporters.py: + validate: launcher: Initialize reporter timer before starting all tests + Patch 1/5 to move logfile handling out of Reporter and into Test. + +2015-01-12 13:09:33 +0100 Ramiro Polla + + * validate/launcher/baseclasses.py: + validate: launcher: Don't wait for processes longer than necessary + +2015-02-03 16:48:49 +0100 Mathieu Duponchelle + + * validate/fault_injection/socket_interposer.c: + socket interposer: Be even more platform restrictive. + +2015-02-03 15:41:01 +0100 Mathieu Duponchelle + + * validate/fault_injection/socket_interposer.c: + validate: do not compile for android. + +2015-01-30 18:52:57 +0100 Mathieu Duponchelle + + * validate/Makefile.am: + * validate/configure.ac: + * validate/fault_injection/Makefile.am: + * validate/fault_injection/socket_interposer.c: + * validate/fault_injection/socket_interposer.h: + * validate/gst/validate/Makefile.am: + * validate/gst/validate/gst-validate-scenario.c: + * validate/tools/Makefile.am: + validate: Implement a fault injection library. + + And implement a corrupt-socket-recv action + + Only compile this on Linux, LD_PRELOAD won't work on Windows. + For now the registering of the action is done through + a call to socket_interposer_init, this will get better + when we refactor the action logic. + https://bugzilla.gnome.org/show_bug.cgi?id=743871 + +2015-01-23 02:04:47 +0100 Mathieu Duponchelle + + * validate/tests/check/validate/padmonitor.c: + validate: tests more issues with caps. + https://bugzilla.gnome.org/show_bug.cgi?id=743387 + +2015-01-23 01:40:59 +0100 Mathieu Duponchelle + + * validate/tests/check/validate/padmonitor.c: + * validate/tests/check/validate/test-utils.c: + validate: Add a test case for caps missing field. + + Make the fake decoder have video/x-raw caps. + https://bugzilla.gnome.org/show_bug.cgi?id=743387 + +2015-01-22 22:29:10 +0100 Mathieu Duponchelle + + * validate/tests/check/validate/padmonitor.c: + validate: prepare tests for port to tracers backend. + https://bugzilla.gnome.org/show_bug.cgi?id=743387 + +2015-01-22 22:07:37 +0100 Mathieu Duponchelle + + * validate/gst/validate/gst-validate-bin-monitor.c: + bin-monitor: add itself as gobject data. + +2015-01-13 02:32:16 +0100 Ramiro Polla + + * validate/launcher/baseclasses.py: + validate: launcher: Fix test log header output + Write log file header before running tests, instead of overwriting the + file afterwards. + https://bugzilla.gnome.org/show_bug.cgi?id=742966 + +2015-01-15 15:32:12 +0100 Ramiro Polla + + * validate/launcher/baseclasses.py: + * validate/launcher/httpserver.py: + * validate/launcher/reporters.py: + validate: launcher: Always create log files + Create log files even when stdout redirection is enabled. + This commit partially reverts 20c28de. + https://bugzilla.gnome.org/show_bug.cgi?id=742973 + +2015-01-15 15:26:14 +0100 Ramiro Polla + + * validate/launcher/baseclasses.py: + * validate/launcher/httpserver.py: + * validate/launcher/main.py: + * validate/launcher/reporters.py: + validate: launcher: Introduce new parameter for log file redirecting + Allow log file redirection through the new --redirect-logs parameter. + Keep the old --logs-dir stdout/stderr parameter, but reset to the + default logs directory in that case, and set redirect_logs internally. + This also prevents the creation of an stdout/stderr directory for + writing xunit.xml. + https://bugzilla.gnome.org/show_bug.cgi?id=742973 + +2015-01-09 14:04:16 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-pad-monitor.c: + pad-monitor: plug caps leak on iterator resync + +2015-01-09 12:36:31 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-pad-monitor.c: + pad-monitor: use the same filter caps when querying downstream caps + To avoid comparing the real result that has been filtered against + a much larger caps that contains all possibilities. + +2014-12-09 10:09:15 +0100 Thibault Saunier + + * validate/tests/check/validate/test-utils.c: + validate: tests: disable g_log handler + It messes up our own failures counter + And pass test-utils into gst-indent + +2014-12-08 18:53:55 -0300 Thiago Santos + + * validate/tests/check/validate/padmonitor.c: + tests: padmonitor: disable glog handling + It messes up our own failures counter + +2014-12-08 17:27:52 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-pad-monitor.c: + pad-monitor: get correct caps to check for proxied fields in caps queries + Elements should proxy the peer element's caps fields and not what they + have currently set on their pads when replying to a caps query + +2014-12-08 17:17:08 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-pad-monitor.c: + pad-monitor: Only add pending caps fields for source pads + As caps events are downstream, caps set travels from sinks to + sources. Adding pending setcaps values to sink pads makes no sense + as when a new caps is set on the sink it would compare with values + currently set on the source pad, causing a critical failure when + renegotiation happens. + +2014-12-08 18:23:10 +0100 Thibault Saunier + + * validate/launcher/baseclasses.py: + validate: launcher: Take the timeout as ref timeout to compute hard_timeout + when it is provided. + +2014-12-08 15:27:54 +0100 Thibault Saunier + + * validate/launcher/apps/gstvalidate.py: + * validate/launcher/baseclasses.py: + validate: launcher: Set a hard timeout on GstValidate tests if we know the duration + +2014-12-08 14:37:15 +0100 Thibault Saunier + + * validate/launcher/baseclasses.py: + validate: launcher: Force kill subprocess when done with them + Making sure that we do not end up having spurious subprocess around + +2014-12-08 08:42:51 -0300 Thiago Santos + + * validate/launcher/baseclasses.py: + launcher: baseclass: add missing parameter + Fixes "NameError: global name 'options' is not defined" + +2014-12-08 10:09:57 +0100 Thibault Saunier + + * validate/launcher/main.py: + validate: launcher: Properly handle non default main dir + for the case of the new testsuite files + +2014-12-07 12:30:25 +0100 Thibault Saunier + + * validate/launcher/apps/__init__.py: + * validate/launcher/apps/gstvalidate.py: + * validate/launcher/main.py: + validate: launcher: Make the gstvalidate application a python module + +2014-12-06 10:53:37 +0100 Thibault Saunier + + * validate/configure.ac: + validate: Remove remaining reference to launcher/apps/validate + It has been removed now. + +2014-12-03 11:28:28 +0100 Thibault Saunier + + * validate/tools/gst-validate.c: + validate: Avoid assert removing an already removed signal handler + And, make sure that we set the return value != 0 when we receive + SIGINT + +2014-12-02 17:32:18 +0100 Thibault Saunier + + * validate/launcher/main.py: + validate: Handle setting the HTTP server local path from testsuites + +2014-12-02 15:41:17 +0100 Thibault Saunier + + * validate/launcher/main.py: + * validate/launcher/utils.py: + validate: Rename gst-qa-assets to gst-integration-testsuites + +2014-12-02 15:39:09 +0100 Thibault Saunier + + * validate/tools/gst-validate.c: + validate: print execution of set_subtitles actions + +2014-12-02 10:02:09 +0100 Thibault Saunier + + * validate/launcher/main.py: + validate: Factor out an LauncherConfig class type to handle configurations + Allowing us to more simply define default value and expose an API on + top of it + +2014-12-02 10:00:42 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + * validate/launcher/apps/gstvalidate.py: + validate: Remove file specific blacklisted tests + +2014-11-29 13:43:06 +0100 Thibault Saunier + + * validate/launcher/baseclasses.py: + validate: Let the user know when new tests are added, or tests are REMOVED + +2014-11-29 00:03:04 +0100 Thibault Saunier + + * validate/launcher/apps/gstvalidate.py: + * validate/launcher/baseclasses.py: + * validate/launcher/main.py: + validate: Add a cleaner API to setup tests in testsuite files + With the testsuite format you will get a setup_tests(tests_manager, + options) function called for each TestManager. + The function will have the exact same role as with old config + file but with a clean API and not magic global variables. + This implies that we need default blacklist to be directly set + on the TestManager and not on options.blacklisted_test + +2014-11-28 22:58:09 +0100 Thibault Saunier + + * validate/launcher/main.py: + validate: Add a way to sync all assets, including big ones + +2014-11-28 22:42:47 +0100 Thibault Saunier + + * validate/launcher/apps/Makefile.am: + * validate/launcher/apps/gstvalidate.py: + * validate/launcher/apps/validate/Makefile.am: + * validate/launcher/apps/validate/validate_testsuite.py: + * validate/launcher/main.py: + * validate/launcher/utils.py: + validate: Remove the default testsuite implementation + The default testsuite implementation should belong to the default + asset repo where we have the corresponding knowledge. + We should style manage a sensible list of known blacklisted tests, + encoding profiles, and generators in GstValidate itself and allow testsuite + actual implementations to easily use them though the register_default_* + methods. + This allow us to be able to remove the ugly execfile() call. + +2014-11-27 12:11:43 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-reporter.c: + validate: Make sure to at least listen to GStreamer and GLib g_logs + If somewhere else someone is overriding the g_log default handler, + we would not get notified of anything. + +2014-11-27 13:48:17 +0100 Thibault Saunier + + * validate/launcher/baseclasses.py: + validate: Disable coloration of GST_DEBUG logs when we have no-color + Do that only when those logs are not saved to a file + +2014-11-26 17:50:11 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/media-descriptor-parser.h: + * validate/gst/validate/media-descriptor.h: + validate: Factor out a function to print action types parametters + + Remove playback-type from the list and just print it + +2014-11-19 17:16:02 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-element-monitor.c: + * validate/gst/validate/gst-validate-element-monitor.h: + * validate/gst/validate/gst-validate-pad-monitor.c: + pad-monitor: do not enforce caps querying rules for converters + Some encoders/decoders can also be converters, do not enforce + caps proxying rules for them + +2014-11-28 11:14:12 +0530 Vineeth T M + + * validate/launcher/main.py: + validate: fix typo in documentation + There are some typing mistakes in gst-validate-launcher --help + Hence fixing the same. + https://bugzilla.gnome.org/show_bug.cgi?id=740833 + +2014-11-25 15:35:09 +0100 Thibault Saunier + + * validate/launcher/apps/validate/validate_testsuite.py: + * validate/launcher/main.py: + validate: Handle unlimited tests duration + Running full length scenario when the user asks + +2014-11-25 15:32:31 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-element-monitor.c: + validate: Already having a monitor is no error + +2014-11-25 15:30:42 +0100 Thibault Saunier + + * validate/pre-commit-python.hook: + validate: pre commit hook: Do not try to run pep8 on non python files! + +2014-11-25 15:29:29 +0100 Thibault Saunier + + * validate/data/Makefile.am: + * validate/data/scrub_backward_seeking_full.scenario: + * validate/data/scrub_forward_seeking_full.scenario: + validate: Add scub_*_seeking_full scenarios + Which basically do the same thing as scrub_*_seeking but during + throughout the whole duration of the media + +2014-11-21 19:35:16 +0100 Thibault Saunier + + * validate/docs/validate/gst-validate-docs.sgml: + * validate/docs/validate/gst-validate-sections.txt: + * validate/gst/validate/gst-validate-reporter.c: + * validate/gst/validate/gst-validate-reporter.h: + * validate/gst/validate/gst-validate-runner.h: + validate: Enhance documentation + +2014-11-19 17:58:23 +0100 Thibault Saunier + + * validate/launcher/apps/gstvalidate.py: + * validate/launcher/main.py: + validate: Add an option to update all .media_info files + +2014-11-16 23:05:45 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate:scenario: Execute actions without playback time without a valid position + If the user did not specify any playback time we should be able to + execute actions even if the pipeline can't answer the position query + + Make simpler to read the conditions of an action execution + +2014-11-09 19:08:52 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-internal.h: + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + validate:scenario: Properly handle ASYNC action execution in the API + The ->execute function now return a GstValidateExecuteActionReturn + which can be set as ASYNC in order to tell the scenario that the action + will be executed asynchronously, when the action is done, the caller is + responsible for calling gst_validate_action_set_done(); so that the + scenario keeps going on. + In this commit we make sure that the old API keeps working as + GST_VALIDATE_EXECUTE_ACTION_ERROR == FALSE and + GST_VALIDATE_EXECUTE_ACTION_OK == TRUE + Morevover GstValidateExecuteActionReturn is just a define + API: + + gst_validate_action_set_done + + GstValidateExecuteActionReturn + https://bugzilla.gnome.org/show_bug.cgi?id=739854 + +2014-11-07 23:19:59 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-internal.h: + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + validate: Add a GstValidateActionTypeFlag flag + Allowing us to define action types more in detail. + Keep backward compatibility, at least with the C API + https://bugzilla.gnome.org/show_bug.cgi?id=739854 + +2014-11-21 14:01:48 +0100 Thibault Saunier + + * validate/gst/validate/Makefile.am: + * validate/gst/validate/gst-validate-bin-monitor.c: + * validate/gst/validate/gst-validate-bin-monitor.h: + * validate/gst/validate/gst-validate-monitor-factory.c: + * validate/gst/validate/gst-validate-pipeline-monitor.c: + * validate/gst/validate/gst-validate-pipeline-monitor.h: + validate: Add a GstValidatePipelineMonitor subclass + We had quite a bit of code dedicated to handled GstPipeline monitoring + inside GstValidateBinMonitor, cleanly split that code into a new object + type + https://bugzilla.gnome.org/show_bug.cgi?id=740704 + +2014-11-20 11:55:45 +0100 Thibault Saunier + + * validate/launcher/apps/gstvalidate.py: + * validate/launcher/baseclasses.py: + * validate/launcher/main.py: + * validate/launcher/utils.py: + validate:launcher: Force clock sync for some protocols + In HLS for example, not having clock sync might lead to races and failures + do not test that for now + +2014-11-20 11:53:34 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-pad-monitor.c: + validate: Don't fail getting master report from a ghostpad without target + +2014-11-19 20:05:57 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-bin-monitor.c: + validate: Print current position even if we do not know the rate + That could cause gst-validate-launcher to wrongly concider tests + as timeout + +2014-11-17 11:39:12 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate:scenario: Force clock sink for scenarios with a pause action + +2014-11-15 18:08:42 +0100 Thibault Saunier + + * validate/launcher/baseclasses.py: + validate: Handle wrong paths when listing avalaible apps + User can make mistake or we can have an empty path. + +2014-11-11 20:56:04 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-pad-monitor.c: + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-report.h: + * validate/tests/check/validate/padmonitor.c: + validate: Do not check if first buffer running time is 0 + It can perfectly not be 0, so it makes no sense to check that. + https://bugzilla.gnome.org/show_bug.cgi?id=739965 + +2014-11-06 23:43:47 +0100 Thibault Saunier + + * validate/launcher/apps/validate/validate_testsuite.py: + validate: Add audiomixer test to the default testsuite + +2014-11-01 09:24:15 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-pad-monitor.c: + pad-monitor: Give better details about segment mismatch issues + +2014-10-30 14:10:33 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-pad-monitor.c: + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-report.h: + validate: Be more precise in issue type for wrong seqnum + Depending on the type of event where the bug occurs, + it is not the same issue type. That allows us to have + much precise reports, and better explain the user + where the issue stands. + +2014-10-16 17:32:56 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-monitor-factory.c: + * validate/gst/validate/gst-validate-scenario.c: + validate: Fix a few annotation issues + +2014-10-15 17:03:48 +0200 Thibault Saunier + + * validate/docs/validate/envvariables.xml: + * validate/gst/validate/gst-validate-scenario.c: + validate: Add the notion of WAIT_MULTIPLIER for the wait action + Allowing the user to decide to wait more, or less, or even not wait + for the wait action to execute when running scenarios. + +2014-09-28 22:37:01 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate:scenario: Report an EXECUTION_ERROR on action execution failure + If the action type handles a better error report type, it should just + return TRUE, and report its issue itself. + +2014-09-19 09:13:13 +0200 Thibault Saunier + + * validate/launcher/baseclasses.py: + validate: Do not exit when we can not discover a result file + Loggable.error actually exit the process, it is not what we want! + + Avoid a backtrace + +2014-11-03 11:50:54 +0100 Edward Hervey + + * validate/gst/validate/gst-validate-scenario.c: + scenario: Allow set-property action to work much earlier + By default an action has no playback-time, this makes it actionable + immediatly. + When no playback-time is set on a set-property action, it will + be activated the moment the element is added in the pipeline. + +2014-10-31 16:01:52 +0100 Edward Hervey + + * validate/gst/validate/gst-validate-bin-monitor.c: + validate-bin-monitor: Initialize local variable + Avoids segfaults when freeing them if they didn't get filled in + +2014-10-26 14:47:12 +0100 Mathieu Duponchelle + + * validate/autogen.sh: + * validate/multi-pre-commit.hook: + * validate/pre-commit-python.hook: + validate: update pre-commit hook. + + Allows to run multiple pre-commit hooks. + + Always relink the hooks on autogen. + + Run pep8 on commited python files. + https://bugzilla.gnome.org/show_bug.cgi?id=739208 + +2014-10-25 14:59:49 +0200 Mathieu Duponchelle + + * validate/launcher/apps/gstvalidate.py: + apps: gstvalidate.py: fix various pyflakes / uncaught pep8 issues. + https://bugzilla.gnome.org/show_bug.cgi?id=739208 + +2014-10-25 14:50:54 +0200 Mathieu Duponchelle + + * validate/launcher/utils.py: + validate-launcher: utils: fix various pyflakes / uncaught pep8 issues. + https://bugzilla.gnome.org/show_bug.cgi?id=739208 + +2014-10-25 14:49:26 +0200 Mathieu Duponchelle + + * validate/launcher/main.py: + validate-launcher: main: fix various pyflakes / uncaught pep8 issues. + https://bugzilla.gnome.org/show_bug.cgi?id=739208 + +2014-10-25 14:46:26 +0200 Mathieu Duponchelle + + * validate/launcher/loggable.py: + validate-launcher: loggable: fix various pyflakes / uncaught pep8 issues. + https://bugzilla.gnome.org/show_bug.cgi?id=739208 + +2014-10-24 14:38:00 +0200 Mathieu Duponchelle + + * validate/launcher/baseclasses.py: + validate-launcher: baseclasses: fix various pyflakes / uncaught pep8 issues. + https://bugzilla.gnome.org/show_bug.cgi?id=739208 + +2014-10-24 14:23:52 +0200 Mathieu Duponchelle + + * validate/launcher/RangeHTTPServer.py: + * validate/launcher/apps/gstvalidate.py: + * validate/launcher/apps/validate/validate_testsuite.py: + * validate/launcher/baseclasses.py: + * validate/launcher/httpserver.py: + * validate/launcher/loggable.py: + * validate/launcher/main.py: + * validate/launcher/reporters.py: + * validate/launcher/utils.py: + validate-launcher: pep8ify sources. + https://bugzilla.gnome.org/show_bug.cgi?id=739208 + +2014-10-23 21:43:45 +0200 Mathieu Duponchelle + + * validate/gst/validate/gst-validate-utils.c: + validate-utils: downgrade ERROR to DEBUG. + This function is called in places where it is legit for it + to return NULL. + +2014-10-23 21:36:03 +0200 Mathieu Duponchelle + + * validate/launcher/baseclasses.py: + launcher: add a way to specify an application directory. + https://bugzilla.gnome.org/show_bug.cgi?id=739091 + +2014-10-23 21:34:27 +0200 Mathieu Duponchelle + + * validate/launcher/apps/Makefile.am: + * validate/launcher/apps/geslaunch.py: + launcher: Don't implement product-specific TestManagers. + This manager will be moved in GES. + https://bugzilla.gnome.org/show_bug.cgi?id=739091 + +2014-10-23 15:21:14 +0200 Mathieu Duponchelle + + * validate/Makefile.am: + * validate/configure.ac: + * validate/launcher/Makefile.am: + * validate/launcher/RangeHTTPServer.py: + * validate/launcher/__init__.py: + * validate/launcher/apps/Makefile.am: + * validate/launcher/apps/geslaunch.py: + * validate/launcher/apps/gstvalidate.py: + * validate/launcher/apps/validate/Makefile.am: + * validate/launcher/apps/validate/validate_testsuite.py: + * validate/launcher/baseclasses.py: + * validate/launcher/httpserver.py: + * validate/launcher/loggable.py: + * validate/launcher/main.py: + * validate/launcher/reporters.py: + * validate/launcher/utils.py: + * validate/tools/Makefile.am: + * validate/tools/gst-validate-launcher.in: + * validate/tools/launcher/Makefile.am: + * validate/tools/launcher/RangeHTTPServer.py: + * validate/tools/launcher/__init__.py: + * validate/tools/launcher/apps/Makefile.am: + * validate/tools/launcher/apps/geslaunch.py: + * validate/tools/launcher/apps/gstvalidate.py: + * validate/tools/launcher/apps/validate/Makefile.am: + * validate/tools/launcher/apps/validate/validate_testsuite.py: + * validate/tools/launcher/baseclasses.py: + * validate/tools/launcher/httpserver.py: + * validate/tools/launcher/loggable.py: + * validate/tools/launcher/main.py: + * validate/tools/launcher/reporters.py: + * validate/tools/launcher/utils.py: + validate-launcher: restructure filesystem + https://bugzilla.gnome.org/show_bug.cgi?id=739091 + +2014-10-24 18:41:30 +0530 Ramprakash Jelari + + * validate/gst/validate/gst-validate-reporter.c: + validate: Fix compiler warning about implicit enum type conversion + gst-validate-reporter.c:119:39: error: implicit conversion from enumeration type + 'GstValidateReportingDetails' to different enumeration type + 'GstValidateInterceptionReturn' [-Werror,-Wenum-conversion] + GstValidateInterceptionReturn ret = GST_VALIDATE_SHOW_UNKNOWN; + ~~~ ^~~~~~~~~~~~~~~~~~~~~~~~~ + gst-validate-reporter.c:124:11: error: implicit conversion from enumeration type + 'GstValidateReportingDetails' to different enumeration type + 'GstValidateInterceptionReturn' [-Werror,-Wenum-conversion] + ret = iface->get_reporting_level (reporter); + ~ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + gst-validate-reporter.c:127:10: error: implicit conversion from enumeration type + 'GstValidateInterceptionReturn' to different enumeration type + 'GstValidateReportingDetails' [-Werror,-Wenum-conversion] + return ret; + ~~~~~~ ^~~ + +2014-10-22 14:16:45 +0200 Mathieu Duponchelle + + * validate/gst/validate/Makefile.am: + build: We install all headers system wide for now. + Will be fixed when the API is deemed stable enough + +2014-10-21 23:31:37 +0200 Mathieu Duponchelle + + * validate/gst/validate/gst-validate-enums.h: + * validate/gst/validate/gst-validate-monitor.c: + * validate/gst/validate/gst-validate-monitor.h: + * validate/gst/validate/gst-validate-pad-monitor.c: + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-report.h: + * validate/gst/validate/gst-validate-reporter.c: + * validate/gst/validate/gst-validate-reporter.h: + * validate/gst/validate/gst-validate-runner.c: + * validate/gst/validate/gst-validate-runner.h: + * validate/tests/check/validate/overrides.c: + * validate/tests/check/validate/padmonitor.c: + * validate/tests/check/validate/reporting.c: + validate: rename GstValidateReportingLevel. + Removes the confusion with GstValidateReportLevel. + Modeled on GstDebugGraphDetails. + +2014-10-18 18:55:59 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-pad-monitor.c: + * validate/tests/check/validate/padmonitor.c: + validate: Verify that elements always send a segment before pushing EOS + EOS is some kind of data flow and thus a segment event should always be + pushed before the EOS is sent + +2014-10-18 18:53:03 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-pad-monitor.c: + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-report.h: + * validate/tests/check/validate/padmonitor.c: + * validate/tests/check/validate/reporting.c: + validate: Properly check that the seqnum of the EOS is always properly set + In the pipeline, an EOS should always have the same seqnum of the + previous SEGMENT event that was received. If the segment is the result + of a seek, it should always be the same as the seek seqnum too. + + (Mathieu Duponchelle): fix reporting and concatenation tests. + +2014-10-03 18:51:17 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-override-registry.c: + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-report.h: + * validate/tests/check/Makefile.am: + * validate/tests/check/validate/overrides.c: + validate: Add support for text based override files + Allowing user to easily determine the severity of issue + types in a config file + https://bugzilla.gnome.org/show_bug.cgi?id=737852 + +2014-10-03 18:53:42 +0200 Thibault Saunier + + * validate/gst/validate/media-descriptor-parser.c: + * validate/gst/validate/media-descriptor-parser.h: + validate: Remove unused method + gst_media_descriptor_add_frame is not used anywhere + https://bugzilla.gnome.org/show_bug.cgi?id=737852 + +2014-10-03 18:42:04 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-utils.c: + * validate/gst/validate/gst-validate-utils.h: + validate: Move GstStructure file parsing into utils + So it can be reused, at least in GstValidate. + +2014-10-02 15:34:28 +0200 Thibault Saunier + + * validate/gst/overrides/gst-validate-default-overrides.c: + * validate/gst/validate/gst-validate-media-info.c: + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-report.h: + * validate/gst/validate/gst-validate-reporter.h: + * validate/gst/validate/media-descriptor-writer.c: + * validate/tests/check/validate/padmonitor.c: + validate: report: Simplify the issue ID registering using GQuarks + + Remove unused issue types + https://bugzilla.gnome.org/show_bug.cgi?id=737790 + +2014-09-15 17:27:54 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-pad-monitor.c: + * validate/gst/validate/gst-validate-pad-monitor.h: + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-report.h: + * validate/tests/check/validate/padmonitor.c: + * validate/tests/check/validate/test-utils.c: + * validate/tests/check/validate/test-utils.h: + * validate/tools/launcher/apps/gstvalidate.py: + validate: Check all buffers when we have the info from MediaDescriptor + We now check that each buffer is the expected one for each buffer that + come into the decoder. + + Fix some minor leaks in test-utils + https://bugzilla.gnome.org/show_bug.cgi?id=736138 + +2014-10-02 11:27:30 +0200 Thibault Saunier + + * validate/gst/validate/media-descriptor-parser.c: + * validate/gst/validate/media-descriptor-parser.h: + validate:media-descriptor-parser: Add a way to create from a string + So it is simple to make use of it from the testsuite + https://bugzilla.gnome.org/show_bug.cgi?id=736138 + +2014-10-01 16:24:58 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-report.c: + report: g_critical are CRITICAL issues! + https://bugzilla.gnome.org/show_bug.cgi?id=736138 + +2014-09-17 17:32:52 +0200 Thibault Saunier + + * validate/tools/launcher/baseclasses.py: + validate: launcher: Fix printing of errors in final report + https://bugzilla.gnome.org/show_bug.cgi?id=736138 + +2014-09-15 19:14:27 +0200 Thibault Saunier + + * validate/tools/launcher/apps/gstvalidate.py: + * validate/tools/launcher/baseclasses.py: + * validate/tools/launcher/main.py: + validate: Add the possibility to generate media infos with frame descs + + Fix a little issue when the generation fails. + https://bugzilla.gnome.org/show_bug.cgi?id=736138 + +2014-09-15 17:26:23 +0200 Thibault Saunier + + * validate/gst/validate/media-descriptor-parser.c: + * validate/gst/validate/media-descriptor-writer.c: + * validate/gst/validate/media-descriptor.h: + validate: MediaDescriptors: Add md5sum to buffer informations + In the media descriptor files, we now have the md5sum of the actual + content of encoded buffers so that we can check that the buffer content is + perfectly what is was supposed to be. + + Fix the check of whether a frame is a keyframe in the string + comparison (g_ascii_strcasecmp return 0 if string matches) + https://bugzilla.gnome.org/show_bug.cgi?id=736138 + +2014-09-15 17:25:14 +0200 Thibault Saunier + + * validate/gst/validate/media-descriptor-parser.h: + validate: Move some method between GstMediaDescriptorParser and GstMediaDescriptor + So that method land where they actually belong. + https://bugzilla.gnome.org/show_bug.cgi?id=736138 + +2014-09-15 17:22:52 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-bin-monitor.c: + * validate/gst/validate/gst-validate-element-monitor.c: + * validate/gst/validate/gst-validate-monitor.c: + * validate/gst/validate/gst-validate-monitor.h: + * validate/tools/gst-validate.c: + validate: Add a way to pass a MediaDescriptor around monitors + And add an option in gst-validate so that the user can define what + media descriptor file to use. + https://bugzilla.gnome.org/show_bug.cgi?id=736138 + +2014-09-12 12:12:14 +0200 Thibault Saunier + + * validate/gst/validate/media-descriptor-writer.c: + validate:media-descriptor: Handle stream with no tags + It was segfaulting before. + +2014-09-17 16:51:20 +0200 Thibault Saunier + + * .gitignore: + * validate/.gitignore: + * validate/docs/.gitignore: + * validate/docs/validate/.gitignore: + validate: Add more files to gitignore + +2014-09-15 17:25:14 +0200 Thibault Saunier + + * validate/gst/validate/media-descriptor-parser.c: + * validate/gst/validate/media-descriptor-parser.h: + * validate/gst/validate/media-descriptor.c: + * validate/gst/validate/media-descriptor.h: + validate: Move some method between GstMediaDescriptorParser and GstMediaDescriptor + So that method land where they actually belong. + +2014-10-12 16:25:25 +0200 Mathieu Duponchelle + + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-report.h: + * validate/gst/validate/gst-validate-reporter.c: + * validate/gst/validate/gst-validate-runner.c: + * validate/tests/check/validate/reporting.c: + validate-report / reporter: rework the way we repeat issues. + + runner: update reports count algorithm. + +2014-10-12 16:13:51 +0200 Mathieu Duponchelle + + * validate/gst/validate/gst-validate-pad-monitor.c: + pad-monitor: mark the peer pad as EOS too. + When a sink pad gets EOS, its src pad monitor should also + be marked as EOS (helpful with issue concatenation). + +2014-10-10 10:22:31 +0200 Mathieu Duponchelle + + * validate/gst/validate/gst-validate-pad-monitor.c: + * validate/gst/validate/gst-validate-runner.c: + * validate/tests/check/validate/reporting.c: + validate-pad-monitor / runner: Check per-object reporting levels. + +2014-10-12 14:36:13 +0200 Mathieu Duponchelle + + * validate/gst/validate/gst-validate-pad-monitor.c: + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-report.h: + validate-report: Set conditions in which a report can't be master. + +2014-10-12 14:34:34 +0200 Mathieu Duponchelle + + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-report.h: + validate-report: Add a reporting level field and setter. + +2014-10-21 19:43:45 +0200 Mathieu Duponchelle + + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-report.h: + * validate/gst/validate/gst-validate-runner.c: + * validate/tests/check/validate/padmonitor.c: + * validate/tests/check/validate/reporting.c: + validate-runner: implement synthetic report. + + Fix criticals logic in validate_runner_printf + + Update padmonitor tests + + Split validate_report_printf function. + +2014-10-10 06:01:03 +0200 Mathieu Duponchelle + + * validate/gst/validate/gst-validate-runner.c: + * validate/tests/check/validate/reporting.c: + validate-runner: Implement REPORT_NONE for global reporting. + Yeah that was tough. Helpful already though, for example: + GST_VALIDATE_REPORT_LEVEL=none,x:all gst-validate src name=x ! sink + will only report issues reported by the source. + + Add test. + +2014-10-10 05:08:28 +0200 Mathieu Duponchelle + + * validate/gst/validate/gst-validate-monitor.c: + * validate/gst/validate/gst-validate-reporter.c: + * validate/gst/validate/gst-validate-reporter.h: + * validate/tests/check/validate/reporting.c: + tests: Check monitors correctly determine their reporting level. + + [API] gst_validate_reporter_get_reporting_level + +2014-10-10 03:55:37 +0200 Mathieu Duponchelle + + * validate/gst/validate/gst-validate-monitor.c: + * validate/gst/validate/gst-validate-runner.c: + validate-runner / monitor: Let the user single out pads. + That's some pretty specific code but it should be helpful. + The following syntax can be used : element-name::pad-name. + + Free return of gst_object_get_name. + +2014-10-10 02:52:26 +0200 Mathieu Duponchelle + + * validate/gst/validate/gst-validate-reporter.c: + * validate/gst/validate/gst-validate-runner.c: + validate-runner / reporter: Sanitize reports refcounting. + The previous code worked but was confusing, the runner didn't actually + take the ref it was releasing later. + + Fix indentation. + +2014-10-10 02:49:54 +0200 Mathieu Duponchelle + + * validate/gst/validate/gst-validate-element-monitor.c: + * validate/tests/check/validate/padmonitor.c: + * validate/tests/check/validate/test-utils.c: + * validate/tests/check/validate/test-utils.h: + tests: Test reports refcounts. + + Set the element monitor on the element as qdata. + +2014-10-10 01:17:43 +0200 Mathieu Duponchelle + + * validate/gst/validate/gst-validate-monitor.c: + * validate/gst/validate/gst-validate-monitor.h: + validate-monitor: Determine the reporting level at setup. + +2014-10-09 19:41:48 +0200 Mathieu Duponchelle + + * validate/gst/validate/gst-validate-runner.c: + * validate/gst/validate/gst-validate-runner.h: + * validate/tests/check/validate/reporting.c: + validate-runner: Add code to parse GST_VALIDATE_REPORT_LEVEL. + + Extend the tests. + + [API] gst_validate_runner_get_default_reporting_level + + [API] gst_validate_runner_get_reporting_level_for_name + +2014-10-08 05:08:21 +0200 Mathieu Duponchelle + + * validate/gst/validate/Makefile.am: + * validate/gst/validate/gst-validate-enums.h: + * validate/gst/validate/gst-validate-runner.c: + * validate/gst/validate/gst-validate-runner.h: + * validate/tests/check/Makefile.am: + * validate/tests/check/validate/reporting.c: + validate-runner: report-level initial work. + + Defines reporting levels and document them. + + Add API to get the default level. + + fix indentation. + + fix some typos. + + Add the beginning of a reporting test. + +2014-10-02 02:50:29 +0200 Mathieu Duponchelle + + * validate/gst/validate/gst-validate-pad-monitor.c: + * validate/tests/check/validate/padmonitor.c: + validate-pad-monitor: concatenate issues. + Fixes https://bugzilla.gnome.org/show_bug.cgi?id=735665 + The process is to check for a similar report in intercept_report on + the pads of the upstream element, set that report as the master report + of the intercepted report, and return REPORTER_KEEP instead + of REPORTER_REPORT. + +2014-10-02 02:34:26 +0200 Mathieu Duponchelle + + * validate/tests/check/validate/test-utils.c: + * validate/tests/check/validate/test-utils.h: + test-utils: add a create_and_monitor element function. + +2014-10-01 18:28:33 +0200 Mathieu Duponchelle + + * validate/gst/validate/gst-validate-reporter.c: + * validate/gst/validate/gst-validate-reporter.h: + validate-reporter: Add some methods + + gst_validate_reporter_get_reports + + gst_validate_reporter_get_reports_count + +2014-10-01 15:53:24 +0200 Mathieu Duponchelle + + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-report.h: + validate-report: Add the notion of master / shadow reports. + A master report is a report that has been detected by a monitor + to stem from the same issue. It thus contains a list of + "shadow reports" which it will browse when printing itself. + +2014-10-01 15:50:11 +0200 Mathieu Duponchelle + + * validate/gst/validate/gst-validate-report.c: + validate-report: Make the ref / unref functions safer. + +2014-10-21 13:07:02 +0200 Mathieu Duponchelle + + * validate/tests/check/validate/padmonitor.c: + tests/padmonitor: Correcly strdup the result of get_metadata. + The const pointer was becoming invalid after the first call to add_metadata, + and we ended up setting corrupted data on the second call. + +2014-10-01 15:11:21 +0200 Mathieu Duponchelle + + * validate/gst/validate/gst-validate-monitor.c: + * validate/gst/validate/gst-validate-pad-monitor.c: + * validate/gst/validate/gst-validate-reporter.c: + * validate/gst/validate/gst-validate-reporter.h: + * validate/gst/validate/gst-validate-scenario.c: + validate-reporter: Add return value to intercept_report. + It will allow to drop, keep or report reports. + +2014-09-30 16:08:46 +0200 Mathieu Duponchelle + + * validate/gst/validate/gst-validate-pad-monitor.c: + validate-pad-monitor: Reimplement reporter interface. + + Do nothing there for now, except chain up. + +2014-09-30 14:52:35 +0200 Mathieu Duponchelle + + * validate/gst/validate/gst-validate-reporter.c: + * validate/gst/validate/gst-validate-reporter.h: + validate-reporter: add gst_validate_reporter_get_report. + + Add locking. + +2014-10-20 13:38:20 +0200 Thibault Saunier + + * validate/configure.ac: + Back to development + +2014-10-20 12:04:25 +0200 Thibault Saunier + + * validate/ChangeLog: + * validate/NEWS: + * validate/configure.ac: + Release 1.4.0 + +2014-10-13 16:28:54 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-bin-monitor.c: + validate: Print position if it could properly be queried + Otherwize we will print meaningless garbage. + +2014-10-13 10:32:07 +0200 Thibault Saunier + + * validate/docs/launcher/conf.py: + * validate/tools/launcher/baseclasses.py: + validate:launcher: Minor enhancement in the documentation + +2014-10-12 20:19:42 +0200 Thibault Saunier + + * validate/data/adaptive_video_framerate.scenario: + * validate/data/adaptive_video_framerate_size.scenario: + * validate/data/adaptive_video_size.scenario: + * validate/data/alternate_fast_backward_forward.scenario: + * validate/data/camerabin_signal.scenario: + * validate/data/disable_subtitle_track_while_paused.scenario: + * validate/data/fast_backward.scenario: + * validate/data/fast_forward.scenario: + * validate/data/force_key_unit.scenario: + * validate/data/pause_resume.scenario: + * validate/data/play_15s.scenario: + * validate/data/reverse_playback.scenario: + * validate/data/scrub_backward_seeking.scenario: + * validate/data/scrub_forward_seeking.scenario: + * validate/data/seek_backward.scenario: + * validate/data/seek_forward.scenario: + * validate/data/seek_forward_backward.scenario: + * validate/data/seek_with_stop.scenario: + * validate/data/simple_seeks.scenario: + * validate/data/switch_audio_track.scenario: + * validate/data/switch_audio_track_while_paused.scenario: + * validate/data/switch_subtitle_track.scenario: + * validate/data/switch_subtitle_track_while_paused.scenario: + * validate/data/update_start.scenario: + * validate/data/update_stop.scenario: + * validate/docs/validate/scenarios.xml: + * validate/gst/validate/gst-validate-scenario.c: + validate: Rename action type playback_time to playback-time + Keeping backward compatiblity with the old naming + +2014-10-12 20:07:58 +0200 Thibault Saunier + + * validate/docs/validate/gst-validate-sections.txt: + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + * validate/tools/gst-validate-transcoding.c: + * validate/tools/gst-validate.c: + validate: Rename gst_validate_add_action_type to gst_validate_register_action_type + The _register naming corresponds much better to what the method does + and makes it more similar to how we refer to this kind of action in + GStreamer. + It is a last minute API change, but that API should not change anymore + after 1.4 is released. + +2014-10-12 20:00:03 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate: Fix the addition of playback_time in the parameter types + +2014-10-12 19:46:39 +0200 Thibault Saunier + + * validate/docs/validate/scenarios.xml: + * validate/tools/gst-validate-transcoding.c: + * validate/tools/gst-validate.c: + validate: Rename --list-action-types to --inspect-action-type + Making clearer the meaning of the parameter and closer to the + usual naming in the GStreamer land. + +2014-10-12 19:16:08 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate: Add the 'flags' for the seek action type + This was always a mandatory field but was not documented + +2014-09-29 10:22:55 +0530 Anuj Jaiswal + + * validate/gst/validate/gst-validate-runner.c: + validate: mishandled pointer criticals + Free glist of criticals + Signed-off-by: Anuj Jaiswal + https://bugzilla.gnome.org/show_bug.cgi?id=736313 + +2014-10-01 10:54:47 +0200 Thibault Saunier + + * validate/docs/validate/command-line-tools.xml: + validate:docs: Add documentation about the default testsuite + +2014-09-30 10:30:24 +0200 Mathieu Duponchelle + + * validate/gst/validate/gst-validate-runner.c: + * validate/gst/validate/gst-validate-runner.h: + * validate/tests/check/validate/padmonitor.c: + validate-runner: switch to using a GList for the reports. + + Return a copy of that list in get_reports. + + update tests. + +2014-09-30 09:24:48 +0200 Mathieu Duponchelle + + * validate/gst/validate/gst-validate-runner.c: + * validate/gst/validate/gst-validate-runner.h: + * validate/tests/check/validate/padmonitor.c: + validate-runner: Hide implementation. + +2014-09-30 09:11:58 +0200 Mathieu Duponchelle + + * validate/gst/validate/gst-validate-runner.c: + * validate/gst/validate/gst-validate-runner.h: + gst-validate-runner: Add locking for the reports list. + +2014-09-29 15:37:40 +0200 Thibault Saunier + + * validate/ChangeLog: + * validate/NEWS: + * validate/configure.ac: + * validate/docs/release.txt: + Release 1.3.90 + +2014-09-12 10:47:18 +0200 Thibault Saunier + + * validate/tools/launcher/apps/gstvalidate.py: + * validate/tools/launcher/baseclasses.py: + validate:launcher: Factorize code to get a MediaDescriptor name for classname + Instead of copy/pasting that code badly + +2014-09-12 10:22:15 +0200 Edward Hervey + + * validate/docs/validate/gst-validate.types: + validate/docs: Add location of Scenario/Action defines + +2014-09-10 16:45:41 +0530 Anuj Jaiswal + + * validate/gst/validate/gst-validate-media-info.c: + validate: (performance issue)refactor to remove duplicate assignment + Signed-off-by: Anuj Jaiswal + https://bugzilla.gnome.org/show_bug.cgi?id=736412 + +2014-09-11 10:54:43 +0200 Thibault Saunier + + * validate/docs/Makefile.am: + validate: docs: Always dist the launcher directoty + Fixing make distcheck + +2014-09-11 09:42:02 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-runner.c: + validate: Get the Runner reports in order of arrival + Making sure they are printed in the right order + +2014-09-10 09:47:22 +0200 Thibault Saunier + + validate: Start a testsuite + Currently implemented tests are: + * Settup and cleanup on monitor is done properly + * Some tests in the PadMonitor are done properly, namely: + - Buffer before segment + - Buffer outside segment + - First buffer running time is always 0 + - The Demuxer flow aggregation is properly checked + https://bugzilla.gnome.org/show_bug.cgi?id=736379 + +2014-09-12 09:49:35 +0200 Edward Hervey + + * validate/gst/validate/gst-validate-internal.h: + validate/private: Avoid double typdef + Instead just include required (public and local) header + gst-validate-scenario.h:43:44: error: redefinition of typedef 'GstValidateActionParameter' is a C11 feature [-Werror,-Wtypedef-redefinition] + +2012-09-26 02:28:00 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + timeline: disable tooltip while scrolling + +2012-09-26 02:00:10 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI/columns.py: + Hide some columns by default + +2012-09-26 01:56:05 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI/columns.py: + columns: optimize cell data functions a little + +2012-09-26 01:41:22 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI/window.py: + Fix crash when copying row to clipboard + +2012-09-24 22:58:58 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + timeline: fix incorrect position after scrolling using the timeline + Apparently events are dropped internally, so the last position after you stop + dragging can be off. + +2012-09-23 16:43:25 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI/models.py: + models: store line offsets in arrays + +2012-09-23 17:22:53 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI/columns.py: + * debug-viewer/GstDebugViewer/GUI/window.py: + * debug-viewer/GstDebugViewer/Plugins/FindBar.py: + Use pango markup instead of attributes + Attributes don't work from introspection, so this blocks porting to gtk3. + In MessageColumn, admit that multiple highlighters don't actually work. + +2012-09-23 17:22:12 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/FindBar.py: + findbar: highlight multiple matches in a message + +2012-09-24 02:15:09 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + timeline: only redraw updated parts of the graph + Improves rendering performance a lot. + +2012-09-24 02:23:22 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + timeline: fix position rectangle missing on first click + Regression caused by previous commit. + +2012-09-22 01:27:37 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + timeline: only redraw required areas when updating position rectangle + +2012-09-22 01:25:22 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI/models.py: + Fix crash when range filtering + Regression from 25cfe9 (timeline: make log level calculation a lot faster). + +2012-09-22 00:33:41 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + timeline: clean up widget drawing + +2012-09-21 22:52:25 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI/models.py: + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + timeline: make log level calculation a lot faster + This is the step where the timeline graph gets colored with the individual log + level colors. It's roughly 4.5 times faster now. Probably can be made even + better, the code also needs a cleanup. + +2012-09-21 22:15:07 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Data.py: + Data: improve stripped log file loading performance + A ~9% improvement for files without colors. This now slightly outperforms the + code before color support was added. + +2012-09-21 21:38:58 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Data.py: + Data: also yield while loading unparseable files + Otherwise, the UI would be blocked while loading something big that is not a + log file at all. + +2012-09-21 19:13:07 +0200 René Stadler + + * debug-viewer/gst-debug-strip-color.py: + Remove color stripping script + +2012-09-21 19:11:40 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Data.py: + Data: add support for colored log files + Adds a ~5% penalty for loading stripped files. + +2012-09-21 00:40:07 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + timeline: remove broken actions from context menu + Hide lines before/after doesn't work as expected in this case. + +2012-09-20 23:51:05 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Data.py: + Data: fix parsing of lines missing filename or function name + E.g. ffmpeg. + +2012-09-20 20:20:58 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + timeline: grab when scrolling in TimelineWidget + Also use gdk_event_request_motions. + +2012-09-20 20:11:48 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + timeline: move mouse handling into TimelineWidget + +2012-09-20 19:58:06 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + timeline: replace self.connect calls with vmethod overrides + +2012-09-17 18:39:53 +0200 Andrzej Bieniek + + * debug-viewer/setup.py: + setup: fix build + +2012-08-27 13:52:56 -0700 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + timeline: don't select row when changing position in the timeline + Behaves just like the scrollbar now. + +2012-08-27 13:46:14 -0700 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + timeline: stop scanning the file while filtering + +2012-08-27 13:45:57 -0700 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + timeline: small cleanup + +2012-08-24 02:09:04 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI/window.py: + * debug-viewer/data/menus.ui: + Modernize menus a little + A bit in preparation to gtk3 app menus. + +2012-08-24 01:50:44 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI/models.py: + * debug-viewer/GstDebugViewer/GUI/window.py: + Simplify and optimize filtered model implementation + RangeFilteredLogModel is gone. The functionality is trivially implemented in + FilteredLogModel now. Changing the range is now O(log n) at worst (was O(n) at + best, for rewriting the arrays). Stacking filtered models is not supported + anymore, which simplifies the code. + +2012-08-24 01:42:00 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI/models.py: + * debug-viewer/GstDebugViewer/GUI/window.py: + Make RangeFilteredLogModel internal to GUI.models + +2012-08-24 01:40:24 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI/models.py: + * debug-viewer/GstDebugViewer/GUI/window.py: + Always use a filtered log model in the log view + Preparing to phase out RangeFilteredLogModel. + +2012-08-24 01:37:27 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI/models.py: + models: cleanup dead code + +2012-03-31 01:16:25 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI/window.py: + window: prevent default handler for delete-event from running + +2012-08-24 01:26:32 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI/window.py: + window: set to insensitive during load/filter operations + +2012-08-24 01:20:05 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI/window.py: + window: show error for unparseable files + +2012-08-24 00:24:55 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI/window.py: + * debug-viewer/data/progress-dialog.ui: + window: replace progress and error dialogs with InfoBars + +2012-08-24 00:10:05 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI/window.py: + window: set wmclass, to have a nicer app name when running uninstalled + +2011-11-16 20:37:21 +0100 René Stadler + + * debug-viewer/GstDebugViewer/Data.py: + Data: fix up out-of-order log lines + This is important because we rely on monotonically increasing timestamps for + binary searches in various places. + Overhead for an already sorted file with 1 million lines is less than 5%. + +2011-11-16 20:23:31 +0100 René Stadler + + * debug-viewer/GstDebugViewer/GUI/window.py: + window: connect action handlers using a function decorator + A bit esoteric, but better than maintaining the list of action names. + +2011-11-16 19:50:06 +0100 René Stadler + + * debug-viewer/GstDebugViewer/Common/GUI.py: + GUI: use 'with' statement + +2011-11-16 19:45:16 +0100 René Stadler + + * debug-viewer/GstDebugViewer/Common/Main.py: + * debug-viewer/GstDebugViewer/Common/utils.py: + * debug-viewer/GstDebugViewer/GUI/window.py: + * debug-viewer/gst-debug-viewer: + Switch to new try..except syntax + This is forward compatible to Python 3. + +2011-11-06 13:35:26 +0100 René Stadler + + * debug-viewer/GstDebugViewer/GUI/columns.py: + * debug-viewer/GstDebugViewer/GUI/window.py: + columns: auto size time column when setting base time + Base time formatting adds + or - in front of the timestamp, so the column has + to grow a little to not hide the last digit. + Also fixes a crash when setting the base time while the time column is hidden. + +2011-11-06 13:19:55 +0100 René Stadler + + * debug-viewer/GstDebugViewer/GUI/columns.py: + columns: also auto size thread and pid column on zoom change + +2011-11-06 13:18:19 +0100 René Stadler + + * debug-viewer/GstDebugViewer/GUI/columns.py: + columns: cleanup default size calculation + Some unused parameters here. + +2011-11-06 12:49:43 +0100 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Timeline: small cleanup + +2011-11-06 12:41:08 +0100 René Stadler + + * debug-viewer/GstDebugViewer/Data.py: + * debug-viewer/tests/create-test-log.py: + Data: remove log line serialization + This is incomplete and prone to error. Move it out into the utility script + (which is the only user). + +2011-11-06 12:19:52 +0100 René Stadler + + * debug-viewer/GstDebugViewer/GUI/colors.py: + * debug-viewer/GstDebugViewer/GUI/columns.py: + Remove odd-even row colors from log level column + This is more of visual clutter than aid. People also seem to be less likely to + spot the connection between the column and the timeline graph colors. + +2011-11-06 00:16:29 +0100 René Stadler + + * debug-viewer/GstDebugViewer/GUI/columns.py: + Resize time and log level columns after zoom change + +2011-11-05 23:47:47 +0100 René Stadler + + * debug-viewer/GstDebugViewer/GUI/app.py: + * debug-viewer/GstDebugViewer/GUI/window.py: + Store zoom level in state + +2011-11-05 23:05:00 +0100 René Stadler + + * debug-viewer/GstDebugViewer/GUI/window.py: + * debug-viewer/data/menus.ui: + Add zoom reset action + +2011-11-05 22:53:24 +0100 René Stadler + + * debug-viewer/data/menus.ui: + Clean up context menu + These actions are not so commonly used, and also are not depending on the + context at all. + +2011-11-05 23:52:40 +0100 René Stadler + + * debug-viewer/GstDebugViewer/GUI/columns.py: + * debug-viewer/GstDebugViewer/GUI/window.py: + Refactor and fix zoom handling + ColumnManager has to apply the zoom factor to newly added columns. Otherwise, + showing a previously hidden column appears with scale 1.0. + This also drops the value-changed signal emission for the vadjustment, as it is + apparently not needed. + +2010-07-06 11:42:08 +0300 Stefan Kost + + * debug-viewer/GstDebugViewer/Data.py: + * debug-viewer/GstDebugViewer/GUI/colors.py: + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Add 'fixme' and 'trace' log levels + +2010-06-30 16:16:45 +0300 Stefan Kost + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + timeline: add tooltip to histogram as well + +2010-04-16 18:26:26 +0300 Stefan Kost + + * debug-viewer/GstDebugViewer/GUI/app.py: + * debug-viewer/GstDebugViewer/GUI/columns.py: + * debug-viewer/GstDebugViewer/GUI/window.py: + * debug-viewer/data/menus.ui: + Add zoom in/out actions, reduce vertical row padding + Add two actions to shrink and enlarge the text in the log pane. Add a theme + overide to set expander size to 1 (see bug #615985) and also turn focus lines + off. Remove extra ypadding on cells. + +2011-09-25 21:38:48 +0200 René Stadler + + * debug-viewer/tests/test_models.py: + Fix tests + Forgot to convert this when modules got split. + +2011-09-11 21:10:47 +0100 Andrzej Bieniek + + * debug-viewer/GstDebugViewer/Main.py: + Fix --version option + +2011-09-09 22:02:28 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI/window.py: + Improve wording of hide lines actions + These also appear in the context menu of the timeline. The more generic wording + makes more sense for the timeline, since you do not pinpoint any specific line + in this case. + +2011-09-09 21:47:16 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + timeline: fix grey background artifact when enlarging window + +2011-09-07 16:11:58 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI/columns.py: + * debug-viewer/GstDebugViewer/GUI/window.py: + * debug-viewer/GstDebugViewer/Plugins/FindBar.py: + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Fix glib warnings on model property access + Seems like pygobject can all of the sudden not handle a NULL model on a + property. Using the getter works around this. Also using the setter now for + consistency. + +2011-09-06 22:27:33 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + timeline: fix possible lag when dragging on timeline + I need to idle-aggregate scroll updates, since gtk performs heavy operations in + a synchronous fashion here (ironically, they do that to make scrolling smooth). + +2010-07-02 23:03:39 +0300 René Stadler + + * debug-viewer/GstDebugViewer/Common/GUI.py: + GUI: Work around GtkBuilder name property API break (gtk+ 2.20) + +2009-10-21 00:32:09 +0300 René Stadler + + * debug-viewer/setup.py: + setup.py: fix installation + +2009-10-21 00:31:46 +0300 René Stadler + + * debug-viewer/GstDebugViewer/Main.py: + Main: fix import + +2009-10-21 00:27:46 +0300 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Remove stale GUI module + +2009-10-16 21:45:29 +0300 René Stadler + + * debug-viewer/GstDebugViewer/Data.py: + * debug-viewer/GstDebugViewer/GUI/columns.py: + * debug-viewer/GstDebugViewer/GUI/window.py: + * debug-viewer/data/menus.ui: + Add new base time feature + The log view context menu gains a new action "Set base time", which changes the + time column to show the delta to the selected row. + +2009-08-07 02:54:10 +0300 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/GstDebugViewer/GUI/__init__.py: + * debug-viewer/GstDebugViewer/GUI/app.py: + * debug-viewer/GstDebugViewer/GUI/colors.py: + * debug-viewer/GstDebugViewer/GUI/columns.py: + * debug-viewer/GstDebugViewer/GUI/filters.py: + * debug-viewer/GstDebugViewer/GUI/models.py: + * debug-viewer/GstDebugViewer/GUI/window.py: + * debug-viewer/GstDebugViewer/Plugins/FindBar.py: + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Split giant GUI module into submodules + +2009-06-13 00:58:36 +0300 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + timeline: fix timestamp binary search + Fixes dragging the mouse over bigger gaps of log activity making the red + position rectangle come out next to the mouse pointer. Also selects the proper + row now, not randomly 1-2 rows before or after the gap. + +2009-06-12 21:53:28 +0300 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + GUI: remove unused imports + +2009-03-14 23:50:03 +0200 René Stadler + + * debug-viewer/setup.py: + setup.py: Fix version number + +2009-03-14 23:02:45 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Common/GUI.py: + * debug-viewer/GstDebugViewer/Common/Main.py: + * debug-viewer/GstDebugViewer/Common/utils.py: + * debug-viewer/GstDebugViewer/Data.py: + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/GstDebugViewer/Plugins/FindBar.py: + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + * debug-viewer/setup.py: + * debug-viewer/tests/test_models.py: + Cleanup whitespace + +2009-03-14 20:40:52 +0200 René Stadler + + * debug-viewer/data/about-dialog.ui: + Update copyright statement in about dialog + +2009-03-14 20:06:16 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/data/gst-debug-viewer.ui: + * debug-viewer/data/menus.ui: + * debug-viewer/setup.py: + Rename UIManager file + +2009-03-14 20:03:37 +0200 René Stadler + + * debug-viewer/data/about-dialog.ui: + * debug-viewer/data/main-window.ui: + * debug-viewer/data/progress-dialog.ui: + Re-write builder files with glade3 + +2009-03-14 19:38:36 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Common/GUI.py: + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/GstDebugViewer/__init__.py: + * debug-viewer/data/about-dialog.ui: + * debug-viewer/data/gst-debug-viewer.glade: + * debug-viewer/data/main-window.ui: + * debug-viewer/data/progress-dialog.ui: + * debug-viewer/setup.py: + Migrate from glade to GtkBuilder + +2009-03-11 00:41:26 +0200 René Stadler + + * debug-viewer/data/gst-debug-viewer.gladep: + Remove glade project file + +2008-11-29 21:06:52 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Common/Main.py: + Fix logging being on by default with recent Python + The fix for Python issue #1021 uncovered a mistake of mine. I was under the + impression that logging.NOTSET level means "off", but in fact it means to not + modify the level, and setting that on the root logger with basicConfig leads to + turning on all levels. + +2008-11-29 21:00:20 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Data.py: + Use mmap in a portable way + +2008-11-26 23:21:57 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Data.py: + * debug-viewer/GstDebugViewer/GUI.py: + Add FIXME comments + +2008-11-26 23:13:05 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + GUI: Fix edit-copy-line action crashing/copying wrong line + When the view was unfiltered, this crashed. When the view was range filtered, + this copied the wrong line. + Spotted by Stefan Kost. + +2008-11-05 00:00:48 +0200 René Stadler + + * debug-viewer/.bzrignore: + * debug-viewer/.gitignore: + Migrate .bzrignore -> .gitignore + +2008-06-30 19:48:34 +0300 René Stadler + + * debug-viewer/GstDebugViewer/Data.py: + * debug-viewer/GstDebugViewer/GUI.py: + Move more attribute lookups out of loops for speed + +2008-06-29 21:14:07 +0300 René Stadler + + * debug-viewer/GstDebugViewer/Data.py: + Add support for recent log format changes, be more tolerant on whitespaces, cope with object names containing '>' + +2008-06-13 22:58:54 +0300 René Stadler + + * debug-viewer/data/gst-debug-viewer.glade: + Use correct license in about dialog + +2008-03-02 15:24:16 +0200 René Stadler + + * debug-viewer/setup.py: + Fix installation by including missing packages + +2008-03-01 20:44:53 +0200 René Stadler + + * debug-viewer/data/gst-debug-viewer.svg: + Add (placeholder) SVG icon file + +2008-02-05 17:29:52 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Update vertical timeline when the widget size changes + +2008-02-04 17:36:57 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Fix bottom view showing the wrong selected log line + +2008-02-04 17:26:48 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Fix bottom view line activating the wrong line after filtering + +2008-01-25 15:44:38 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Fix bottom view not showing current line until you add something there + +2008-01-25 12:40:51 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Faster handling of partial expose events in timeline + +2008-01-25 11:17:02 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Main.py: + Mention GStreamer in --help output + +2008-01-25 11:12:48 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Allow to cancel a running filter process + +2008-01-24 16:19:15 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Make hide before/after action insensitive when first/last line is selected + +2008-01-24 15:18:37 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/data/gst-debug-viewer.ui: + Add filtering for object name and source code filename + +2008-01-24 14:29:39 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Add FIXME comment + +2008-01-24 12:16:41 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/data/gst-debug-viewer.ui: + Create own menu structure for log view context menu + +2008-01-24 11:49:41 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Sync show-timeline action state before connecting signal handler + +2008-01-24 11:47:27 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Timeline.py: Move per-window management into own class + +2008-01-24 11:12:05 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Re-format long line + +2008-01-24 10:59:14 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Replace gdk.ALL_EVENTS_MASK with proper minimal set of event flags + +2008-01-23 17:13:07 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/tests/performance.py: + Move performance test program into its own file + +2008-01-23 17:07:55 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/data/gst-debug-viewer.ui: + Allow creation of more than one window + +2008-01-23 17:07:51 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Add TODO comment + +2008-01-23 14:51:14 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Data.py: + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Use GdkColors for level column, cleanup color handling + +2008-01-23 11:03:47 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Replace linear-time filtered index search with usage of bisect module + +2008-01-22 16:28:09 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Restore visible range of log view when changing filter + +2008-01-22 13:50:04 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Resolve small FIXME in SubRange + +2008-01-22 13:40:36 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Cleanup FilteredLogModel.super_model_changed_range + +2008-01-22 12:59:37 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/tests/test_models.py: + Fix crash with unparsable files + +2008-01-22 11:22:38 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Common/Main.py: + Make option parser work with glib before 2.13.2 + +2008-01-21 14:45:02 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + s/get_cells/get_cell_renderers/ again + +2008-01-21 13:24:02 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Data.py: + Correctly parse categories with digits in them (fixes flump3dec, v4l2src messages) + +2008-01-21 11:15:42 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/tests/test_models.py: + Fix filtered range transformation (finally!) + +2008-01-11 11:11:00 +0200 René Stadler + + * debug-viewer/tests/test_models.py: + Add simple identity filter model tests + +2008-01-10 16:15:53 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/tests/test_models.py: + Fix filtered model index translation and improve tests + +2008-01-10 14:14:12 +0200 René Stadler + + * debug-viewer/tests/test_models.py: + Add test suite for filtered models + +2008-01-10 14:12:34 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Fix off-by-one error in filtered model range reclamping + +2008-01-10 13:49:58 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Improve filtered model interacting with range changes + +2008-01-02 20:54:33 +0100 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Unify two very similar methods + +2007-12-21 15:10:15 +0100 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Add FIXME about broken index translation logic + +2007-12-18 18:48:28 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Prevent crash with older bindings + +2007-12-18 17:10:08 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Fix more problems when clamping with filter turned on + +2007-12-18 15:26:05 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Fix off-by-one error causing display of spurious line when clamping with filter turned on + +2007-12-18 13:46:55 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Fix selection of line after changing filter, add logging + +2007-12-17 17:50:10 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Preserve clamped timestamp range when filtering and vice versa + +2007-12-13 13:43:28 +0200 René Stadler + + * debug-viewer/gst-debug-strip-color.py: + Fix color stripping script + +2007-12-13 13:36:45 +0200 René Stadler + + * debug-viewer/gst-debug-strip-color.py: + Add gst-debug-strip-color.py, a script to strip color codes + +2007-12-12 18:35:28 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Implement idle filtering (with progress display) + +2007-12-12 16:22:51 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/data/gst-debug-viewer.glade: + Factor out progress dialog handling into its own reusable object class + +2007-12-12 14:59:53 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Implement category filtering + +2007-12-12 13:49:02 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Allow for more than one (log level) filter to be set + +2007-12-11 11:38:45 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/FindBar.py: + Restore search to a consistent state when showing the search bar again + +2007-12-11 11:28:17 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/FindBar.py: + Add accelerators to search result navigation actions + +2007-12-11 11:16:44 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/FindBar.py: + Fix search result navigation action sensitivity when showing the find bar + +2007-12-11 11:13:46 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/FindBar.py: + Add search navigation menu items to view menu + +2007-12-11 10:44:20 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Common/GUI.py: + When right clicking to open a context menu, pass the event on (which selects the row) + +2007-12-10 17:40:31 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/FindBar.py: + Implement backward search result navigation + +2007-12-10 17:09:07 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/FindBar.py: + Remove dead/useless code + +2007-12-10 17:06:23 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/FindBar.py: + Rename variable + +2007-12-10 17:04:47 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/FindBar.py: + Implement lazy searching + +2007-12-10 14:22:51 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/GstDebugViewer/Plugins/FindBar.py: + Some search fixes + +2007-12-10 11:49:39 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/FindBar.py: + * debug-viewer/GstDebugViewer/Plugins/__init__.py: + Add status label to find bar + +2007-12-07 16:50:02 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Fix timeline warning/error indicator triangle vertical position + +2007-12-07 16:24:01 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Common/GUI.py: + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Add GUI utility function to add a popup menu to a widget + +2007-12-07 14:10:03 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Cleanup timeline warning/error triangle drawing, add TODOs + +2007-12-07 12:02:15 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Add tooltip to vertical timeline widget + +2007-12-07 11:25:30 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Add hide before/after menu items to timeline context menu + +2007-12-06 17:51:33 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/data/gst-debug-viewer.ui: + Add context menu to bottom view, with entry to clear all lines + +2007-12-04 16:34:53 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Fix copying of line to clipboard + +2007-12-04 16:21:45 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Remove implicit keybinding of copy message action + +2007-12-04 14:44:34 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Fix adding rows to the bottom view + +2007-12-04 14:40:41 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Fix crash when adding a line to bottom view with log filter turned on + +2007-12-04 14:35:50 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Implement filtered log model index translation + +2007-12-04 14:22:19 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Add support for stacking log model filters + +2007-12-03 17:49:04 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Do not let the user add duplicate lines to the bottom log view + +2007-12-03 17:44:40 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Keep the bottom view sorted by timestamp + +2007-12-03 16:38:29 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Unbreak filtering again + +2007-12-03 16:07:05 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/GstDebugViewer/Plugins/FindBar.py: + Improve method to update log view after search text change + +2007-12-03 15:47:58 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/FindBar.py: + Remove dead code + +2007-12-03 15:45:09 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Data.py: + * debug-viewer/GstDebugViewer/GUI.py: + Use the mmapped fileobj in more places and use slice access + +2007-12-03 15:24:20 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Add simple cache eviction to LazyLogModel to limit memory usage + +2007-12-03 14:58:04 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/FindBar.py: + Rename show-find-bar action callback handler + +2007-12-03 12:18:23 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/GstDebugViewer/Plugins/FindBar.py: + Implement lazy highlighting of search results + +2007-12-03 11:46:44 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/FindBar.py: + Move search matching logic into the SearchOperation object + +2007-12-03 11:35:31 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/FindBar.py: + Add search result navigation + +2007-11-30 17:41:33 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Fix crash when showing all lines after having filtered down to zero lines + +2007-11-30 17:39:36 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Avoid GtkWarning when filtering down to no visible line at all + +2007-11-30 17:33:08 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Fix crash when displaying only one line + +2007-11-30 17:14:36 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Hide unimplemented filtering actions + +2007-11-30 17:13:12 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Add preliminary log level filtering support + +2007-11-30 16:44:36 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/data/gst-debug-viewer.ui: + Add level, category, object filtering actions + +2007-11-30 16:01:51 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Derive range/clamping model filter from the new base class + +2007-11-30 16:00:09 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Add identity filter model to save some memory + +2007-11-30 15:47:51 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Add base class for filtered log models + +2007-11-30 15:38:20 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Move class around + +2007-11-30 15:35:05 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Change code to cleaner terminology of filter model relationships + +2007-11-30 14:15:32 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Try to avoid a crash regarding illegal paths received from GtkTreeView + +2007-11-30 14:05:18 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Fix timeline level distribution plotting after gaps + +2007-11-30 10:54:32 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + For search highlighting, use pango attrlists instead of markup + +2007-11-30 10:31:45 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + After changing the filter, scroll to the selected row + +2007-11-30 10:21:38 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Retain bottom view model and fix crash after filter change + +2007-11-29 17:28:35 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Keep line selection when changing filter model + +2007-11-29 16:21:38 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Only auto size view columns once + +2007-11-29 15:49:58 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/data/gst-debug-viewer.glade: + Remove redundant property settings + +2007-11-29 15:40:51 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/data/gst-debug-viewer.glade: + Fix window size and position state persistency + +2007-11-29 15:34:35 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Fix hanging after loading an unparsable/colored file + +2007-11-29 15:25:31 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Behave a little better with unparsable/colored files + +2007-11-29 15:11:40 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Fix division by zero crash with unparsable/colored files + +2007-11-29 15:03:07 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Data.py: + Fix crash when viewing a colored log file + +2007-11-29 14:29:10 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Fix crash when opening a file that has trash lines only + +2007-11-29 14:26:56 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Data.py: + Gracefully handle garbage lines at the line cache level + +2007-11-29 14:07:31 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Correct wording in benchmark output + +2007-11-29 13:53:42 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Fix comment + +2007-11-29 13:51:46 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Fix level distribution calculation for the last partition + +2007-11-29 13:31:54 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Make message column receive a minimal size, which removes the size warning + +2007-11-29 13:26:54 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Remove more outdated comments + +2007-11-29 13:15:28 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Remove outdated comment + +2007-11-29 13:09:20 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Remove commented code + +2007-11-29 11:38:39 +0200 René Stadler + + * debug-viewer/data/gst-debug-viewer.ui: + Add separator to view menu + +2007-11-29 11:37:51 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + * debug-viewer/data/gst-debug-viewer.ui: + s/omit lines/hide lines/ + +2007-11-29 11:20:34 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + After load, select the first line + +2007-11-29 11:19:00 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + When navigating with the timeline, select the line in the center of the view + +2007-11-29 11:16:34 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + When navigating with the bottom view, select the target line + +2007-11-29 11:09:28 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + When activating a bottom view row, navigate the log view there + +2007-11-29 10:31:59 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Add ability to add rows to bottom view + +2007-11-28 16:10:57 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Avoid copies of the line index list in the line view model (fixes range filtering) + +2007-11-28 15:58:28 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Remove left over whitespace + +2007-11-28 15:32:06 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/data/gst-debug-viewer.glade: + Display timestamp and full message of selected line + +2007-11-28 11:27:26 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Common/GUI.py: + * debug-viewer/GstDebugViewer/GUI.py: + Correctly set sensitivity of row action group + +2007-11-28 10:57:02 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Add a (pretty limited) context menu to the timeline widget + +2007-11-28 10:42:46 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Adjust comment + +2007-11-28 10:27:45 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Common/Main.py: + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/GstDebugViewer/Main.py: + Fix handling of filename command line argument + +2007-11-28 09:56:35 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Tweak thread colors a bit + +2007-11-27 17:11:28 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Add fuzzy compatibility to unpatched pygtk 2.12.0 + +2007-11-27 16:50:41 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + * debug-viewer/data/gst-debug-viewer.ui: + Add ranged line omission feature + +2007-11-27 13:47:30 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/FindBar.py: + Speed up immediate search results by setting search start position + +2007-11-27 13:30:28 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Common/Data.py: + Use low idle priority for dispatching, to fix initial vtimeline display + +2007-11-27 12:03:32 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/GstDebugViewer/Plugins/FindBar.py: + Add basic search highlighting + +2007-11-26 18:01:30 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Draw vertical timeline connectors as triangles + +2007-11-26 16:55:11 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Set view selection mode to BROWSE + +2007-11-26 16:52:21 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Fix timeline position drawing and vertical timeline initial display + +2007-11-26 15:42:44 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Put basename of open file into window title + +2007-11-26 15:31:13 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Data.py: + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/data/gst-debug-viewer.ui: + Add reload file functionality + +2007-11-26 14:55:31 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Disable interactive search on the log view + +2007-11-26 14:42:46 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Common/GUI.py: + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/GstDebugViewer/Plugins/FileProperties.py: + * debug-viewer/GstDebugViewer/Plugins/FindBar.py: + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + * debug-viewer/GstDebugViewer/Plugins/__init__.py: + Save state of timeline visibility + +2007-11-26 13:55:03 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Common/GUI.py: + * debug-viewer/GstDebugViewer/GUI.py: + Refactor state/config classes to be more flexible + +2007-11-26 11:06:31 +0200 René Stadler + + * debug-viewer/data/gst-debug-viewer.ui: + Comment out unimplemented new-window action item + +2007-11-26 10:53:37 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Gracefully handle errors when opening a file + +2007-11-26 09:47:53 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/FindBar.py: + * debug-viewer/data/gst-debug-viewer.glade: + Add very simple search bar + +2007-11-23 16:06:10 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Colorize vertical timeline lines to indicate different threads + +2007-11-23 15:04:14 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Add (commented) support to draw the vertical timeline on first display + +2007-11-23 11:46:43 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + * debug-viewer/data/gst-debug-viewer.glade: + Add vertical timeline widget (which looks quite cool) + +2007-11-22 20:44:02 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + s/get_cells/get_cell_renderers/ + +2007-11-22 16:27:34 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/data/gst-debug-viewer.ui: + Replace filename column with code column, listing filename and line number + +2007-11-22 16:06:55 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + * debug-viewer/GstDebugViewer/Plugins/__init__.py: + Make file->open work correctly + +2007-11-22 13:48:47 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Enable double-clicking a file in file chooser dialog + +2007-11-22 13:36:13 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Align log level column text in center + +2007-11-22 13:35:39 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Fix initial column size measurement + +2007-11-22 11:56:34 +0200 René Stadler + + * debug-viewer/tests/create-test-log.py: + Adjust test log generator + +2007-11-22 11:03:09 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Clamp timeline mouse position to actual range + +2007-11-22 10:47:06 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Common/Data.py: + * debug-viewer/GstDebugViewer/Common/GUI.py: + * debug-viewer/GstDebugViewer/Common/Main.py: + * debug-viewer/GstDebugViewer/Common/__init__.py: + * debug-viewer/GstDebugViewer/Common/utils.py: + * debug-viewer/GstDebugViewer/Data.py: + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/GstDebugViewer/Main.py: + * debug-viewer/GstDebugViewer/Plugins/ColorizeRows.py: + * debug-viewer/GstDebugViewer/Plugins/FileProperties.py: + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + * debug-viewer/GstDebugViewer/Plugins/__init__.py: + * debug-viewer/GstDebugViewer/__init__.py: + * debug-viewer/gst-debug-viewer: + * debug-viewer/setup.py: + Fix copyright/license headers and module docstrings + +2007-11-22 10:33:18 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/GstDebugViewer/Plugins/__init__.py: + Cleanup + +2007-11-22 10:29:23 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + * debug-viewer/GstDebugViewer/Plugins/__init__.py: + Display timeline by default + +2007-11-22 10:19:36 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Fix missing the last vertical ref line in the timeline display + +2007-11-22 09:56:21 +0200 René Stadler + + * debug-viewer/.bzrignore: + Add .bzrignore file + +2007-11-22 09:55:13 +0200 René Stadler + + * debug-viewer/data/gst-debug-viewer.glade.bak: + Kick glade backup file out of the repo + +2007-11-22 09:54:10 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/data/gst-debug-viewer.glade: + * debug-viewer/data/gst-debug-viewer.glade.bak: + Implement cancelling of the load process in the UI + +2007-11-22 09:31:37 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Progressively draw the debug level distribution into the timeline widget + +2007-11-21 17:40:31 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Common/Data.py: + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Don't make timeline data processing block the GUI + +2007-11-21 15:21:40 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Common/__init__.py: + * debug-viewer/GstDebugViewer/GUI.py: + Ease importing of modules from the Common package + +2007-11-21 14:21:38 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Data.py: + Ditch arrays for offset storage again + +2007-11-21 13:42:32 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Fix timeline for files where the first timestamp >> 0 + +2007-11-21 11:40:13 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Data.py: + Use an array for line offset mapping (if file < 4GB) + +2007-11-21 10:47:40 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Data.py: + Add LogLines class + +2007-11-20 17:45:35 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Rename COL_LINE to COL_LINE_NUMBER + +2007-11-20 17:40:35 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Data.py: + Fix object name serialization + +2007-11-20 15:58:52 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Data.py: + * debug-viewer/tests/create-test-log.py: + Add test script to generate a simple test log. Fix level name space adjustment + +2007-11-20 15:32:14 +0200 René Stadler + + * debug-viewer/MANIFEST.in: + * debug-viewer/po/POTFILES.in: + Add dummy po directory and add MANIFEST.in + +2007-11-20 15:25:32 +0200 René Stadler + + * debug-viewer/gst-debug-viewer: + * debug-viewer/gst-debug-viewer.desktop: + * debug-viewer/gst-debug-viewer.desktop.in: + * debug-viewer/gst-debug-viewer.py: + * debug-viewer/setup.cfg: + * debug-viewer/setup.py: + Copy over distutils setup from gst-inspector + +2007-11-20 14:52:26 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Data.py: + * debug-viewer/GstDebugViewer/GUI.py: + Almost allow copying a full line to clipboard + +2007-11-20 13:58:34 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Remove commented code + +2007-11-20 13:56:15 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Replace model.get with model.get_value + +2007-11-20 13:34:00 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Cleanup + +2007-11-20 13:31:58 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Remove commented code, resolve FIXME + +2007-11-20 12:33:47 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Simplify function + +2007-11-20 11:06:27 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Rename more density stuff to timeline + +2007-11-19 15:55:08 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Remove unused attribute + +2007-11-19 15:52:01 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Data.py: + And now make it actually run\! + +2007-11-19 15:44:54 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Also commit the previous change to the GUI module :-/ + +2007-11-19 15:27:16 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Data.py: + * debug-viewer/GstDebugViewer/GUI.py: + Move final log line parsing from GUI to Data module + +2007-11-19 11:35:27 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Data.py: + Optimize color stripping function a bit + +2007-11-19 10:59:52 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + Also plot green info line count in timeline display + +2007-11-17 10:23:58 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Add user/system time to benchmark output + +2007-11-17 10:06:09 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/LineFrequency.py: + * debug-viewer/GstDebugViewer/Plugins/Timeline.py: + * debug-viewer/data/gst-debug-viewer.ui: + Rename line frequency plugin/widget to timeline + +2007-11-16 17:25:08 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/LineFrequency.py: + Speed up level density sentinel + +2007-11-16 16:30:17 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Data.py: + * debug-viewer/GstDebugViewer/GUI.py: + Correctly handle variable length thread address formatting + +2007-11-16 15:56:57 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/LineFrequency.py: + Add markers for warning and error log messages to the timeline display + +2007-11-16 15:06:59 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/GstDebugViewer/Plugins/LineFrequency.py: + Colorize log and debug log levels in frequency display widget + +2007-11-16 13:26:20 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Colorize debug level column + +2007-11-16 12:53:02 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Data.py: + * debug-viewer/GstDebugViewer/GUI.py: + Rename debug level instances + +2007-11-16 12:48:08 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Data.py: + * debug-viewer/GstDebugViewer/GUI.py: + Hacky commit to parse debug level on line cache level + +2007-11-16 11:03:22 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Data.py: + Add (commented out) support for parsing debug level at line cache scan time + +2007-11-16 10:28:23 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/GstDebugViewer/Plugins/LineFrequency.py: + Cleanup + +2007-11-15 18:17:28 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Fix timestamps of unparsable lines to fix line density display + +2007-11-15 18:12:57 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Data.py: + * debug-viewer/GstDebugViewer/GUI.py: + Fix message display + +2007-11-15 17:54:30 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Data.py: + * debug-viewer/GstDebugViewer/GUI.py: + Save huge amounts of memory by never caching the message and interning data for the other columns + +2007-11-15 15:06:37 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Data.py: + * debug-viewer/GstDebugViewer/GUI.py: + Use less regex matching to parse lines (does not provide a performance gain though) + +2007-11-15 14:07:00 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Make column id order match log line fields order + +2007-11-15 14:01:53 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Fix benchmark hack option + +2007-11-15 13:47:38 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/data/gst-debug-viewer.ui: + Add some simple filtering + +2007-11-15 09:20:34 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Add evil comment about treeview slowness with multiple selection mode + +2007-11-15 08:58:48 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Fix comment + +2007-11-14 22:51:47 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Use monospace font for some numeric columns + +2007-11-14 20:35:18 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/GstDebugViewer/Plugins/LineFrequency.py: + Also draw vertical help lines + +2007-11-14 16:56:35 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Change model design to be more filter friendly + +2007-11-14 15:49:03 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/LineFrequency.py: + Draw horizontal helper lines in frequency display + +2007-11-14 15:44:01 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Separate log model into base class and lazy implementation. Add basis for a filter model based on that + +2007-11-14 14:49:55 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Remove dead/commented out code + +2007-11-14 14:48:31 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Add/change comments + +2007-11-14 13:57:08 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Change view columns menu item label + +2007-11-14 13:34:53 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/data/gst-debug-viewer.ui: + Add PID column + +2007-11-14 13:15:36 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/FileProperties.py: + * debug-viewer/data/gst-debug-viewer.ui: + Add skeleton for file properties plugin + +2007-11-14 12:48:43 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/data/gst-debug-viewer.ui: + Add debug output. Add filename column + +2007-11-14 11:31:57 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Add view columns visibility and ordering state persistence + +2007-11-14 11:13:07 +0200 René Stadler + + * debug-viewer/GstDebugViewer/GUI.py: + Don't make column headers clickable + +2007-11-14 10:55:12 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Data.py: + * debug-viewer/GstDebugViewer/GUI.py: + Fix progress display on load + +2007-11-14 10:44:08 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Plugins/LineFrequency.py: + If the indicator in the frequency display is just 1px wide, don't use transparency + +2007-11-14 10:30:19 +0200 René Stadler + + * debug-viewer/GstDebugViewer/Common/Data.py: + * debug-viewer/GstDebugViewer/Common/GUI.py: + * debug-viewer/GstDebugViewer/Common/Main.py: + * debug-viewer/GstDebugViewer/Common/__init__.py: + * debug-viewer/GstDebugViewer/Common/utils.py: + * debug-viewer/GstDebugViewer/Data.py: + * debug-viewer/GstDebugViewer/GUI.py: + * debug-viewer/GstDebugViewer/Main.py: + * debug-viewer/GstDebugViewer/Plugins/ColorizeRows.py: + * debug-viewer/GstDebugViewer/Plugins/LineFrequency.py: + * debug-viewer/GstDebugViewer/Plugins/__init__.py: + * debug-viewer/GstDebugViewer/__init__.py: + * debug-viewer/data/gst-debug-viewer.glade: + * debug-viewer/data/gst-debug-viewer.glade.bak: + * debug-viewer/data/gst-debug-viewer.gladep: + * debug-viewer/data/gst-debug-viewer.png: + * debug-viewer/data/gst-debug-viewer.ui: + * debug-viewer/gst-debug-viewer.desktop: + * debug-viewer/gst-debug-viewer.py: + * debug-viewer/pixmaps/gst-debug-viewer.png: + New import (the old repo got busted, just had 4 revs anyways) + +2014-09-06 12:34:39 +0200 Thibault Saunier + + * validate/configure.ac: + * validate/docs/Makefile.am: + validate: Make sphinx documentation generation optionnal + +2014-09-06 11:41:48 +0200 Thibault Saunier + + * validate/configure.ac: + * validate/docs/Makefile.am: + * validate/docs/launcher/Makefile.am: + * validate/docs/launcher/conf.py: + * validate/docs/launcher/index.rst: + * validate/docs/launcher/launcher.rst: + * validate/docs/launcher/modules.rst: + validate:launcher: Add needed files to build documentation with sphinx + +2014-09-06 11:38:38 +0200 Thibault Saunier + + * validate/tools/launcher/apps/Makefile.am: + * validate/tools/launcher/apps/ges-launch.py: + * validate/tools/launcher/apps/geslaunch.py: + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/apps/gstvalidate.py: + validate: launcher: Cleanup and rename apps to avoid '-' in their name + +2014-09-06 10:02:13 +0200 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + validate:launcher: Avoid '.' before media file extension in test classnames + +2014-09-05 19:47:00 +0000 Felix Schwarz + + * validate/docs/validate-design.txt: + * validate/docs/validate-usage.txt: + * validate/docs/validate/command-line-tools.xml: + * validate/docs/validate/envvariables.xml: + * validate/docs/validate/scenarios.xml: + validate:docs: fix spelling mistakes + https://bugzilla.gnome.org/show_bug.cgi?id=736160 + +2014-09-05 23:15:29 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + * validate/tools/gst-validate-transcoding.c: + * validate/tools/gst-validate.c: + validate: Take a const gchar ** in gst_validate_print_action_types + This is what we actually need and thus is cleaner. + +2014-09-05 23:03:58 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-internal.h: + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + * validate/tools/gst-validate-transcoding.c: + * validate/tools/gst-validate.c: + validate: Implement the notion of implementer namespace to the action types + This allows users to know who implements an action type. + + Enhance the printing of all action making it readable. + +2014-09-05 19:30:52 +0200 Thibault Saunier + + * validate/tools/gst-validate.c: + validate: Add informations on the switch-track action overrided for playbin + +2014-09-04 23:54:34 +0200 Thibault Saunier + + * validate/docs/validate/Makefile.am: + * validate/docs/validate/command-line-tools.xml: + * validate/docs/validate/envvariables.xml: + * validate/docs/validate/gst-validate-docs.sgml: + * validate/docs/validate/scenarios.xml: + * validate/gst/validate/gst-validate-scenario.c: + * validate/tools/gst-validate-transcoding.c: + * validate/tools/gst-validate.c: + validate: docs: Add some GstValidate usage documentation + + Fix minor issues in the gst-validate and gst-validate-transcoding + tools documentation + +2014-09-04 11:54:41 +0200 Guillaume Desmottes + + * validate/gst/validate/gst-validate-monitor-factory.c: + validate: remove redundant pre-condition in monitor_factory_create + The same check is already done at the head of the function. + https://bugzilla.gnome.org/show_bug.cgi?id=736019 + +2014-09-04 11:53:56 +0200 Guillaume Desmottes + + * validate/gst/validate/gst-validate-report.h: + * validate/gst/validate/gst-validate-runner.c: + validate: fix a couple of typos in comments + https://bugzilla.gnome.org/show_bug.cgi?id=736019 + +2014-09-04 19:18:25 +0200 Thibault Saunier + + * validate/docs/validate-usage.txt: + validate:docs: Sensibly update the usage file + +2014-08-22 19:30:14 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate: Use a GList to store action types instead of hashtable + It is more adapted and allows us to print the action types in a stable + maneer. + +2014-08-22 18:45:13 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-report.c: + validate: report: Do not repeat type name when printing its details + +2014-08-19 11:10:57 +0200 Thibault Saunier + + * validate/configure.ac: + validate: Change the version to 1.0.0.1 + The 1.0.0.1 means that it is targetting the GStreamer 1.X serie, + and is a git version (thus 0.1) + GstValidate will most probably not be released and we should try to + be able to use it with as many version of the GStreamer 1.X serie + as possible. + +2014-08-18 18:41:50 +0200 Thibault Saunier + + * validate/Makefile.am: + * validate/configure.ac: + * validate/docs/Makefile.am: + * validate/docs/validate/Makefile.am: + * validate/docs/validate/gst-validate-docs.sgml: + * validate/docs/validate/gst-validate-sections.txt: + * validate/docs/validate/gst-validate.types: + * validate/docs/version.entities: + * validate/docs/version.entities.in: + * validate/gst/validate/gst-validate-internal.h: + * validate/gst/validate/gst-validate-monitor-factory.c: + * validate/gst/validate/gst-validate-runner.c: + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + * validate/gst/validate/validate.c: + validate: Document the API with gtk-doc + +2014-06-11 09:23:11 +0200 Thibault Saunier + + * validate/gst/validate/Makefile.am: + validate: Add GObject Introspection support + +2014-08-14 10:55:44 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-internal.h: + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + * validate/tools/gst-validate-transcoding.c: + * validate/tools/gst-validate.c: + validate: Rework the action parameter API + Making it possible to properly define parameters, and describe them. + + Document all action types! + +2014-08-13 23:07:47 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-internal.h: + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + * validate/gst/validate/validate.c: + * validate/tools/gst-validate-transcoding.c: + * validate/tools/gst-validate.c: + validate: Add an option to print all avalaible actions with details + + Cleanup actions descriptions + + Make GstValidateActionType internal only and only expose the structure + +2014-08-14 10:57:33 +0200 Thibault Saunier + + * validate/tools/gst-validate.c: + validate: Use the buffering mode to see if pipeline is live or not + +2014-08-14 10:56:56 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-pad-monitor.c: + validate: Do not segfault when receiving a segment on unlink pad + For some reason we did no discover that before. + +2014-08-13 20:47:24 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + validate: Make GstValidateActionType a GstMiniObject and expose it in the API + +2014-08-13 20:46:17 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + validate:scenario: Cleanup header and add some padding to classes + Let's start making gst-validate ABI and API stable + +2014-08-20 18:59:26 +0530 Anuj Jaiswal + + * validate/tools/gst-validate.c: + gst-validate: fix some minor memory leaks + https://bugzilla.gnome.org/show_bug.cgi?id=735099 + +2014-08-19 18:06:14 +0200 Mathieu Duponchelle + + * validate/tools/launcher/apps/gst-validate.py: + validate: generate test names with the stream_info filename. + And not with the contained uri string, which is variable. + +2014-08-12 15:14:28 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate: The scenario only old a weak ref so unref the weak ref + We were unrefing an object we did not actually own a ref on. + +2014-08-12 09:36:34 +0200 Thibault Saunier + + * validate/tools/gst-validate.c: + validate: Print when we set pipeline state because of buffering + +2014-08-11 20:19:02 +0200 Thibault Saunier + + * validate/configure.ac: + * validate/tools/gst-validate-launcher.in: + * validate/tools/launcher/apps/Makefile.am: + * validate/tools/launcher/apps/validate/Makefile.am: + * validate/tools/launcher/baseclasses.py: + * validate/tools/launcher/main.py: + validate:launcher: Properly handle libsdir when gst-validate is installed + + Fix the _in_devel function + + Install the validate default testsuite implementation in the right + place + +2014-08-11 13:21:09 +0200 Thibault Saunier + + * validate/tools/launcher/baseclasses.py: + validate:launcher: Let testsuite know the actual file in which they are + +2014-08-11 13:19:22 +0200 Thibault Saunier + + * validate/tools/launcher/baseclasses.py: + validate:launcher: Add the logic of needed env variables in tests + +2014-08-10 12:41:57 +0200 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + validate:launcher: Expose all classes to be used to create testsuites + To create testsuite from outside gst-validate, the user will need to be + able to use the TestGenerator and subclasses of Test that we implement + in the apps, to do so we publicly expose them in the TestManager class + so that user have acces to everything they need. + +2014-08-10 12:04:31 +0200 Thibault Saunier + + * validate/tools/launcher/baseclasses.py: + * validate/tools/launcher/httpserver.py: + * validate/tools/launcher/main.py: + * validate/tools/launcher/reporters.py: + validate:launcher: Handle stdout/stderr as possible logfiles + Allowing people to get all the logs in the terminal + +2014-08-09 23:22:39 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-report.c: + validate: Allow several outputs in GST_VALIDATE_FILE + +2014-08-09 16:34:09 +0200 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/utils.py: + validate: Launcher: Add support for the dash protocol + And make sure that the HTTP server is started if it is needed to serve + some HLS or DASH streams + +2014-08-08 19:14:02 +0200 Thibault Saunier + + * validate/tools/launcher/main.py: + validate:Launcher: Use the first media path as a path for http server + We need to have a default path and the first one sounds like a + reasonnable default. + +2014-08-08 12:33:54 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-bin-monitor.c: + * validate/gst/validate/gst-validate-bin-monitor.h: + * validate/tools/launcher/RangeHTTPServer.py: + * validate/tools/launcher/httpserver.py: + * validate/tools/launcher/main.py: + validate:launcher: Allow limitating local HTTP server bandwith + By default we limit its bandwith to 1MBps which is somehow similare to a + good internet connection case. + +2014-08-05 18:51:20 +0200 Thibault Saunier + + * validate/data/switch_audio_track_while_paused.scenario: + * validate/tools/launcher/apps/validate/validate_testsuite.py: + * validate/tools/launcher/baseclasses.py: + validate:launcher: Disable racy HLS tests + + Add need-clock-synk to switch_audio_track_while_paused as it relies on + the clock sync to pause and then display subtitles + +2014-08-05 10:59:21 +0200 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/apps/validate/validate_testsuite.py: + validate:launcher: Take into account exitcode in transcoding tests + And disable a few racy tests that were not detected because of that + +2014-07-29 12:17:21 +0200 Thibault Saunier + + * validate/tools/launcher/apps/validate/validate_testsuite.py: + validate:launcher:testsuite: De activate backward playback where appropriate + And re activate it where it works + +2014-07-26 11:42:09 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate: Actually accept rounding errors and small mistakes for position + WHen seeking in paused the position right after should be pretty much + the exact one, but sometimes it can be a little different because of + rounding issues and similare. + +2014-07-26 11:41:09 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-report.h: + * validate/gst/validate/gst-validate-runner.c: + * validate/gst/validate/gst-validate-runner.h: + validate: Add a way to avoid printing all the issue in reports + Avoiding user to be flooded by information he does not want while + debugging + +2014-07-26 08:27:55 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-report.h: + * validate/gst/validate/gst-validate-reporter.c: + validate: Print the report when aborting because of an issue + Letting a chance to the user to know what bug he faced! + +2014-07-24 19:26:29 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-runner.c: + * validate/gst/validate/gst-validate-scenario.c: + validate: Enhance output about critical errors + +Lower some warning to INFO + +2014-07-24 19:02:38 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-override-registry.c: + * validate/gst/validate/gst-validate-override-registry.h: + * validate/gst/validate/gst-validate-override.c: + * validate/gst/validate/gst-validate-scenario.c: + validate: Allow overrides for scenario issues + +2014-07-21 18:00:42 +0200 Thibault Saunier + + * validate/tools/launcher/reporters.py: + validate: Avoid readding several time the same test in the tests result list + +2014-07-19 11:47:44 +0200 Thibault Saunier + + * validate/tools/launcher/baseclasses.py: + validate:launcher: Fix test number print + +2014-07-31 17:54:17 +0200 Thibault Saunier + + * validate/tools/launcher/apps/validate/validate_testsuite.py: + tools: Launcher: Disable validate.file.*.simple.scrub_forward_seeking.synchronized + It is still a bit racy and sometimes the seek just does not happen + +2014-07-31 17:43:51 +0200 Thibault Saunier + + * validate/tools/launcher/apps/validate/validate_testsuite.py: + tools: launcher: Disable subtitle track switching scenario on Sintel + It is racy at the moment. + +2014-07-23 20:39:05 +0200 Thibault Saunier + + * validate/data/change_state_intensive.scenario: + * validate/data/fast_backward.scenario: + * validate/data/fast_forward.scenario: + * validate/tools/launcher/baseclasses.py: + validate:launcher: Allow informing minimum media duration in scenarios + Allowing the launcher to avoid running tests on medias that are not long + enough + +2014-07-23 17:49:21 +0200 Thibault Saunier + + * validate/tools/launcher/main.py: + * validate/tools/launcher/reporters.py: + validate:launcher: Always print final report + enhance output + +2014-07-23 14:51:43 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate:scenario: Properly check that remaining actions are not 'ending' ones + When checking that all action were executed, we need to make sure that + actions such as EOS or stop are not taken into account as we might have + shorter medias than the duration of the scenario, and that should not be + fatal. + + Plug a leak on the way + +2014-07-23 14:43:29 +0200 Thibault Saunier + + * validate/data/change_state_intensive.scenario: + * validate/data/fast_backward.scenario: + * validate/data/fast_forward.scenario: + * validate/data/seek_backward.scenario: + * validate/data/seek_forward.scenario: + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/baseclasses.py: + validate: launcher: Force clock syncronization for some scenarios + In some cases it is necessary that the clock is sync so that all the + actions can be executed. + +2014-07-23 10:54:37 +0200 Thibault Saunier + + * validate/tools/launcher/baseclasses.py: + * validate/tools/launcher/main.py: + validate: Launcher: Make sure tests are always executed in same order + +2014-07-22 11:42:48 -0300 Thiago Santos + + * validate/data/camerabin_signal.scenario: + * validate/gst/validate/gst-validate-scenario.c: + gst-validate-scenario: add emit-signal + emit-signal action allows to emit signals to elements in scenarios. + The implementation only accepts signals without arguments for now but + it can be extended to use parameters if needed in the future + +2014-07-22 15:49:09 +0200 Thibault Saunier + + * validate/tools/launcher/baseclasses.py: + validate: Launcher: Fix a backtrace using self in a @staticmethod + +2014-07-21 22:41:28 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-scenario.c: + gst-validate-scenario: the structure has the type + Get the GValue directly from the structure and do not assume everything + is stored as a string and use the GstStructure's GValue to set the property + to the instances + +2014-07-21 22:01:27 -0300 Thiago Santos + + * validate/tools/gst-validate.c: + gst-validate: properly set pipeline to null before unref + In case it fails when going ready->paused it will remain in ready state + and be unref'd in ready, leading to an assertion + +2014-07-21 19:09:24 +0200 Arnaud Vrac + + * validate/configure.ac: + * validate/gst/validate/Makefile.am: + validate: Fix build on some custom platforms + We need to explicitely pass GLIB_LIBS for GModule as it seems not to be included by + GST_ALL_LIBS and we need LIBM + +2014-05-19 18:06:46 +0200 Lubosz Sarnecki + + * validate/tools/launcher/httpserver.py: + httpserver: launch webserver with the same python interpreter. + +2014-07-19 09:48:17 +0200 Thibault Saunier + + * validate/tools/gst-validate.c: + validate: Dot the pipeline on interuption + +2014-07-18 15:57:24 +0200 Aurélien Zanelli + + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-report.h: + validate: duplicate strings in gst_validate_issue_new() + Do this to avoid discarding 'const' qualifier when using it with + constant strings. Moreover it will avoid a g_free on constant string. + https://bugzilla.gnome.org/show_bug.cgi?id=733362 + +2014-07-18 16:28:49 +0200 Thibault Saunier + + * validate/tools/launcher/baseclasses.py: + validate:launcher: Fix a backtrace using an undefined method + +2014-07-17 16:48:21 +0200 Thibault Saunier + + * validate/tools/launcher/baseclasses.py: + valdate:launcher: Do not refer to self in @staticmethod + There is no self in there. + +2014-07-17 16:44:08 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate: Add a set-debug-threshold scenario action + Allowing users to activate the debug only at the interesting time + +2014-07-17 16:42:02 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-report.c: + validate: Add Gst debugging when using gst-validate printing feature + Giving usefull debugging informations in the GSt debug logs + +2014-07-17 12:17:31 +0200 Thibault Saunier + + * validate/tools/gst-validate.c: + validate: Do not auto flush pipeline bus + We want to see all messages in our async handler + And flush it when we are done. + +2014-07-16 19:38:01 +0200 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + validate:launcher: Avoid using sync=true on fakesinks + Making the test run much faster! + +2014-07-16 19:37:35 +0200 Thibault Saunier + + * validate/tools/launcher/reporters.py: + validate: Launcher: Print total time spent in the final report + +2014-07-16 18:21:16 +0200 Thibault Saunier + + * validate/data/switch_subtitle_track_while_paused.scenario: + validate: Make switch_subtitle_track_while_paused handle states + +2014-07-16 14:46:32 +0200 Thibault Saunier + + * validate/data/disable_subtitle_track_while_paused.scenario: + vaildate: Make disable_subtitle_track_while_paused handle states + +2014-07-13 18:21:50 +0200 Thibault Saunier + + * validate/data/scrub_forward_seeking.scenario: + validate: Make scrub_forward_seeking handle states + +2014-07-16 13:54:54 +0200 Thibault Saunier + + * validate/tools/launcher/baseclasses.py: + validate:launcher: Properly check that encoded files have the exact wanted format + +2014-07-16 12:50:41 +0200 Thibault Saunier + + * validate/tools/launcher/apps/ges-launch.py: + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/baseclasses.py: + validate:launcher: Add a method to create a GstValidateMediaDescriptor from a uri + +2014-07-16 12:16:03 +0200 Thibault Saunier + + * validate/tools/launcher/apps/ges-launch.py: + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/baseclasses.py: + * validate/tools/launcher/utils.py: + validate:launcher: Move MediaFormatCombination to baseclasses.py + + Add some simple helpers + +2014-07-16 12:03:14 +0200 Thibault Saunier + + * validate/tools/launcher/apps/ges-launch.py: + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/baseclasses.py: + * validate/tools/launcher/utils.py: + validate:launcher: Implement a GstValidateEncodingTestInterface class + Allowing code to be shared between apps that run rendering tests + +2014-07-16 11:39:08 +0200 Thibault Saunier + + * validate/tools/launcher/apps/ges-launch.py: + validate:launcher: Implement a MediaDescriptor subclass for xges project files + +2014-07-16 11:36:29 +0200 Thibault Saunier + + * validate/tools/launcher/baseclasses.py: + validate:launcher: Make a MediaDescriptor baseclass to be used by any application + +2014-07-16 10:35:34 +0200 Thibault Saunier + + * validate/tools/launcher/main.py: + validate:launcher: Give information to users when cloning asset failed + It might not be obvious from the stacktrace so it is better to clearly + explain what the failure was when we know it + +2014-07-16 10:16:19 +0200 Thibault Saunier + + * validate/tools/launcher/main.py: + validate:launcher: Fixup the default asset update command + +2014-07-16 10:12:04 +0200 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/utils.py: + validate:launcher: Generate proper EncodingProfiles for audio/video only media files + +2014-07-16 10:10:44 +0200 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/baseclasses.py: + validate:launcher: Move the MediaDescriptor class to the baseclasses.py file + +2014-07-16 10:09:32 +0200 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + validate:launcher: Allow transcoding audio only files 5 time longer than long_limit + Transcoding audio is a lot shorter so we can concider that transcoding files that are only + only is 5 time shorter than the actual file (empirical number) + +2014-07-16 10:03:11 +0200 Thibault Saunier + + * validate/tools/gst-validate-media-check.c: + validate:media-check: Pass the GError where needed. + +2014-07-15 12:16:34 +0200 Thibault Saunier + + * validate/gst/validate/media-descriptor-writer.c: + validate: Avoid segfault in the error path + +2014-07-15 11:59:23 +0200 Thibault Saunier + + * validate/gst/validate/media-descriptor-writer.c: + validate:media-descriptor-writer: Handle medias with 1 single stream + +2014-07-08 13:50:11 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate: Check that after a seek in PAUSED position is perfect + In case of ACCURATE seeking, the position after a SEEK in PAUSED state + should be *exactly* the one requested by the user. + +2014-07-11 15:45:18 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-scenario.c: + scenario: add set_property scenario action + Allows setting element's properties during a scenario. Very useful + for testing that elements behave correctly when changing properties + during playing state + https://bugzilla.gnome.org/show_bug.cgi?id=733070 + +2014-07-09 19:10:57 +0300 Sreerenj Balachandran + + * codecanalyzer/.gitignore: + * codecanalyzer/AUTHORS: + * codecanalyzer/COPYING: + * codecanalyzer/Makefile.am: + * codecanalyzer/NEWS: + * codecanalyzer/README.md: + * codecanalyzer/autogen.sh: + * codecanalyzer/configure.ac: + * codecanalyzer/data/Makefile.am: + * codecanalyzer/data/pixmaps/Makefile.am: + * codecanalyzer/data/pixmaps/codecanalyzer-logo.png: + * codecanalyzer/data/pixmaps/frame-thumbnail.png: + * codecanalyzer/data/ui/LICENSE.txt: + * codecanalyzer/data/ui/Makefile.am: + * codecanalyzer/data/ui/mainwindow.xml: + * codecanalyzer/data/ui/menu.xml: + * codecanalyzer/src/Makefile.am: + * codecanalyzer/src/codecanalyzer.c: + * codecanalyzer/src/gst_analyzer.c: + * codecanalyzer/src/gst_analyzer.h: + * codecanalyzer/src/plugins/Makefile.am: + * codecanalyzer/src/plugins/gst/Makefile.am: + * codecanalyzer/src/plugins/gst/analyzersink/Makefile.am: + * codecanalyzer/src/plugins/gst/analyzersink/analyzer_utils.c: + * codecanalyzer/src/plugins/gst/analyzersink/analyzer_utils.h: + * codecanalyzer/src/plugins/gst/analyzersink/gstanalyzersink.c: + * codecanalyzer/src/plugins/gst/analyzersink/gstanalyzersink.h: + * codecanalyzer/src/plugins/gst/analyzersink/mpeg_xml.c: + * codecanalyzer/src/plugins/gst/analyzersink/mpeg_xml.h: + * codecanalyzer/src/plugins/gst/analyzersink/plugin.c: + * codecanalyzer/src/plugins/gst/analyzersink/xml_utils.c: + * codecanalyzer/src/plugins/gst/analyzersink/xml_utils.h: + * codecanalyzer/src/xml_parse.c: + * codecanalyzer/src/xml_parse.h: + New Tool: Add a CodecAnalyzer + https://bugzilla.gnome.org/show_bug.cgi?id=731853 + +2014-07-07 16:12:22 +0200 Stefan Sauer + + * mediainfo/TODO: + * mediainfo/src/mi-info.vala: + TODO: add some planning comments + +2013-10-22 10:57:14 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + mi-info: add wikilink for opus + +2014-07-02 17:53:55 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate: Execute actions if we get seeked in ready state + +2014-07-02 11:27:22 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-bin-monitor.c: + * validate/gst/validate/gst-validate-scenario.c: + * validate/tools/gst-validate.c: + validate: Let scenarios tell the apps about whether it handles states + The user only needs to add handles-states=true in the description line + of the scenario + +2014-06-19 12:58:49 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate:scenario: Do not care about the position if we are not at least in PAUSED state + At that time the position query will be meaningless so we should just go to the next + action. + +2014-06-26 15:07:39 +0200 Thibault Saunier + + * validate/tools/launcher/apps/validate/validate_testsuite.py: + validate: Handle MXF files + +2014-06-26 15:03:07 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-report.h: + * validate/gst/validate/media-descriptor.c: + validate:media-check: Changes in tags detection are not fatal issues + +2014-06-26 13:01:13 +0200 Thibault Saunier + + * validate/tools/launcher/apps/Makefile.am: + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/apps/validate/validate_testsuite.py: + * validate/tools/launcher/apps/validate_default_testsuite.py: + validate:launcher Add video mixing tests + + Move default_testsuite.py to validate_testsuite.py as we are now + exposing tests that are not enabled by default + +2014-06-26 12:42:38 +0200 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/apps/validate_default_testsuite.py: + * validate/tools/launcher/baseclasses.py: + * validate/tools/launcher/main.py: + validate:launcher: Move the notion of test generator to the baseclasses + This can be very usefull for all the TestManager and thus exposes a + higher level API for test writers. + +2014-06-19 16:26:43 +0200 Thibault Saunier + + * validate/tools/launcher/apps/Makefile.am: + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/apps/validate_default_testsuite.py: + validate:launcher: Move default testsuite to a dedicated file + Making the separation cleaner between the launcher and the test + implementation + +2014-06-20 19:01:41 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate: Properly handle CLOCK_TIME_NONE position and duration values + In the value parser. + +2014-06-19 13:03:48 +0200 Thibault Saunier + + * validate/tools/gst-validate-transcoding.c: + * validate/tools/gst-validate.c: + validate: Print the return value at the end + Making it easier to know whether the test passed or not. + +2014-06-19 12:56:34 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate:scenario: Move the check about whether we are still seeking upper in the function + Avoiding to try to get position and do operations on a pipeline that is seeking + +2013-11-25 13:55:10 +0000 Vincent Penquerc'h + + * validate/gst/validate/gst-validate-scenario.c: + validate-scenario: only use valid position/duration + Position/duration query may fail, or yield unknown values (eg, + unknown duration for live streams). In these cases, we must ensure + we do not use those invalid values. + https://bugzilla.gnome.org/show_bug.cgi?id=715160 + +2014-06-19 09:38:52 +0200 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + validate:launcher: Not concider all scenarios by default with --wanted-test + Instead let the users activate that with -t ALL + +2014-06-19 09:22:36 +0200 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/main.py: + validate:launcher: Let the user set user options in the config file + +2014-06-18 17:27:09 +0200 Thibault Saunier + + * validate/tools/launcher/main.py: + validate:launcher: Use RawTextHelpFormatter to (not) format user help + +2014-06-18 17:26:05 +0200 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/baseclasses.py: + * validate/tools/launcher/main.py: + validate:launcher: Add a way to create test suite outside the three + + Make sure to namespace the API + + Remove cruft about G_V_PROTOCOL_VIDEO_RESTRICTION_CAPS + +2014-06-18 13:02:53 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate:scenario: We are not changing state if the set_state failed. + +2014-06-18 13:02:29 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate:scenario: Add a debug category and add some debug + +2014-06-18 13:01:42 +0200 Thibault Saunier + + * validate/gst/validate/media-descriptor-writer.c: + validate: media-descirptor: Add more infos about discoverer error + +2014-06-18 12:51:02 +0200 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + validate:launcher: Cleanup the way we generate tests adding the notion of TestGenerator + Making it easier to extend the testsuite. + +2014-06-18 15:57:14 +0200 Thibault Saunier + + * validate/tools/gst-validate.c: + validate:tools: Dot the pipeline on usefull places + Meaning on warning and state changes. + +2014-06-28 12:33:45 +0200 Sebastian Dröge + + * validate/gst/validate/gst-validate-reporter.c: + validate: Don't call gst_debug_log_valist() if debugging is disabled + And also stop leaking a string every time. + +2014-06-28 11:36:27 +0200 Sebastian Dröge + + * validate/tools/Makefile.am: + gst-validate: Add $(GIO_LIBS) and $(GIO_CFLAGS) as required + +2014-06-17 15:10:41 +0200 Thibault Saunier + + * validate/tools/gst-validate-launcher.in: + validate: Fix launcher when running installed + +2014-06-17 14:17:21 +0200 Thibault Saunier + + * validate/data/Makefile.am: + validate: scenarios: Install play_15s.scenario + +2014-06-16 16:47:18 +0200 Thibault Saunier + + * validate/tools/launcher/reporters.py: + validate:launcher:reporter: Sort Final report by results + +2014-06-16 16:46:21 +0200 Thibault Saunier + + * validate/tools/launcher/apps/ges-launch.py: + * validate/tools/launcher/utils.py: + validate:launcher:ges: Fix rendered duration checking + +2014-06-16 16:40:10 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-pad-monitor.c: + validate: pad-monitor: Do not compare not fixed sinkpad caps fields + We are only able to check that the sink pad caps values are inside the src pad + value. + +2014-06-16 08:49:22 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + scenario: Do not be so tolerant about seek drift + +2014-06-03 09:38:29 +0200 Edward Hervey + + * validate/gst/validate/gst-validate-pad-monitor.c: + pad-monitor: Handle out-of-segment first buffer + If the initial buffer is before segment.start, we don't want to raise + the "first buffer doesn't have 0 running-time" issue. + Also add debug for tracking issues + +2014-06-03 10:02:10 +0200 Edward Hervey + + * validate/gst/validate/gst-validate-bin-monitor.c: + * validate/gst/validate/gst-validate-media-info.c: + * validate/gst/validate/gst-validate-override-registry.c: + * validate/gst/validate/gst-validate-pad-monitor.c: + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-reporter.c: + * validate/gst/validate/gst-validate-utils.c: + * validate/gst/validate/media-descriptor.c: + * validate/tools/gst-validate-media-check.c: + * validate/tools/gst-validate-transcoding.c: + * validate/tools/gst-validate.c: + validate: Run gst-indent on all code + so whitespace. much indent. spacing ! + +2014-05-27 12:30:54 +0200 Thibault Saunier + + * validate/configure.ac: + validate: Depend at least on GLib 2.36 + +2014-05-19 19:42:46 +0200 Thibault Saunier + + * validate/data/Makefile.am: + * validate/data/scrub_backward_seeking.scenario: + * validate/tools/launcher/apps/ges-launch.py: + validate: Add a scrub_backward_seeking scenario + + Make use of it in ges-launch and do not try to seek while playing in + GES as it is not supported yet + +2014-05-24 01:28:36 -0400 Nicolas Dufresne + + * validate/gst/validate/gst-validate-scenario.c: + validate: Don't pass NULL to g_strsplit + +2014-05-22 16:13:31 +0200 Edward Hervey + + * validate/gst/validate/gst-validate-utils.c: + validate-utils: Fix unitialized variable + gst-validate-utils.c:413:7: error: variable 'v0' is used uninitialized whenever 'if' condition is true [-Werror,-Wsometimes-uninitialized] + if (c == '!') { + ^~~~~~~~ + gst-validate-utils.c:424:10: note: uninitialized use occurs here + return v0; + ^~ + gst-validate-utils.c:413:3: note: remove the 'if' if its condition is always false + if (c == '!') { + ^~~~~~~~~~~~~~~ + gst-validate-utils.c:411:13: note: initialize the variable 'v0' to silence this warning + gdouble v0; + ^ + = 0.0 + 1 + +2014-05-21 11:50:09 +0200 Thibault Saunier + + * validate/configure.ac: + * validate/gst/Makefile.am: + * validate/gst/overrides/Makefile.am: + * validate/gst/overrides/gst-validate-default-overrides.c: + * validate/gst/preload/Makefile.am: + * validate/gst/preload/gst-validate-monitor-preload.c: + * validate/gst/validate/Makefile.am: + * validate/gst/validate/gst-validate-default-overrides.c: + * validate/gst/validate/gst-validate-monitor-preload.c: + validate: Move overrides and preload libraries to dedicated folders + This way it is cleaner and it is simpler to handle the various compilation dependencies. + +2014-05-16 16:20:26 +0200 Lubosz Sarnecki + + * validate/tools/gst-validate-launcher.in: + * validate/tools/launcher/RangeHTTPServer.py: + * validate/tools/launcher/__init__.py: + * validate/tools/launcher/apps/ges-launch.py: + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/baseclasses.py: + * validate/tools/launcher/httpserver.py: + * validate/tools/launcher/main.py: + * validate/tools/launcher/reporters.py: + * validate/tools/launcher/utils.py: + python: change shebangs to python2 + +2014-05-15 09:46:24 +0200 Thibault Saunier + + * validate/gst/validate/media-descriptor-parser.c: + * validate/gst/validate/media-descriptor-writer.c: + validate: Properly use boolean in XML + +2014-05-08 17:48:39 +0200 Thibault Saunier + + * validate/gst/validate/media-descriptor-writer.c: + validate: Improve perf when writing the XML file + + Pass the file into gst-indent + +2014-05-07 13:14:51 +0200 Thibault Saunier + + * validate/data/Makefile.am: + * validate/data/change_state_intensive.scenario: + * validate/tools/launcher/apps/gst-validate.py: + validate: Add a scenario that switches state many intensively + + Use it by default in the launcher tests + +2014-05-07 12:43:53 +0200 Thibault Saunier + + * validate/tools/launcher/main.py: + validate: Minor fix for blacklisted test output formatting + +2014-05-07 12:21:49 +0200 Thibault Saunier + + * validate/tools/launcher/main.py: + validate:launcher: Add an option to only launch the http server + +2014-05-07 12:21:30 +0200 Thibault Saunier + + * validate/tools/launcher/baseclasses.py: + validate: Keep scenario discovering logs in a file + +2014-05-07 11:34:47 +0200 Thibault Saunier + + * validate/data/fast_forward.scenario: + validate: Avoid using stop value in the fast_forward scenario + +2014-05-07 11:30:39 +0200 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + validate: Properly check that outputed videos have a correct duration + +2014-05-07 11:30:09 +0200 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/baseclasses.py: + * validate/tools/launcher/main.py: + * validate/tools/launcher/utils.py: + validate: Add the notion of "long" tests so that we can avoid some test to be run if they are too long + +2014-05-07 09:51:19 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + scenario: Add a general action to set state + +2014-05-07 09:50:28 +0200 Thibault Saunier + + * validate/autogen.sh: + validate: Properly set the pre commit hook + +2014-05-07 09:46:28 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate:scenario: Pass into gst-indent + +2014-05-07 09:15:34 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate: The wait mandatory field is duration + +2014-05-07 09:11:12 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate: Make sure mandatory fields are present when parsing scenarios + +2014-05-06 15:34:08 +0200 Mathieu Duponchelle + + * validate/gst/validate/gst-validate-bin-monitor.c: + * validate/gst/validate/gst-validate-bin-monitor.h: + * validate/gst/validate/gst-validate-scenario.c: + scenarios: add a stateless property. + This property enables the user to have actions executed independently + of the state of the pipeline. + Conflicts: + validate/gst/validate/gst-validate-scenario.c + +2014-05-05 17:00:45 +0200 Mathieu Duponchelle + + * validate/gst/validate/gst-validate-scenario.c: + scenario: make sure to not execute actions when changing state. + Conflicts: + validate/gst/validate/gst-validate-scenario.c + +2014-05-04 09:30:14 +0200 Thibault Saunier + + * validate/tools/gst-validate.c: + validate: The 'buffering' variable needs to be static + We need its value between bus_callback calls to be the same + +2014-05-02 17:25:07 -0400 Luis de Bethencourt + + * validate/tools/gst-validate.c: + gst-validate: some static variables can be local + buffering is only used inside the bus_callback, so it can have that local + scope. same thing with ret which is only used in the main function. + +2014-05-02 16:53:51 -0400 Luis de Bethencourt + + * validate/tools/gst-validate.c: + gst-validate: small typo in usage summary + +2014-05-02 20:05:28 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate: Wait for the PAUSED state to be reached before executing actions + +2014-05-02 19:00:49 +0200 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + validate: Fix the name of the sintel blacklisting + +2014-05-02 18:50:41 +0200 Thibault Saunier + + * validate/data/Makefile.am: + * validate/data/disable_subtitle_track_while_paused.scenario: + validate: Add a scenario to disable subtitle track while paused + +2014-05-02 14:06:18 +0200 Thibault Saunier + + * validate/gst/validate/media-descriptor-writer.c: + * validate/gst/validate/media-descriptor-writer.h: + * validate/gst/validate/media-descriptor.h: + * validate/tools/gst-validate-media-check.c: + validate: Implement frame by frame writing in the media descriptor writer + + Add an option to fully parse media files in the gst-validate-media-check tool + +2014-05-01 14:58:14 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate: Execute position right when the pipeline reaches PAUSED + We might go to PAUSED SYNC if nothing happens in the pipeline + +2014-05-01 14:11:24 +0200 Thibault Saunier + + * validate/tools/gst-validate.c: + * validate/tools/launcher/apps/gst-validate.py: + validate: Add a scenario that disable subtitles + + Clean the sythax to define switch-track action that actually + desactivate the track + +2014-05-01 12:52:09 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate: Do not execute action when buffering + While buffering we should no try to execute anything as we would not be + controlling properly the execution. + + Activate scrub forward seeking for HTTP streams + +2014-05-01 12:34:35 +0200 Thibault Saunier + + * validate/data/Makefile.am: + * validate/data/switch_set_external_subtitle.scenario: + * validate/data/switch_subtitle_track_while_paused.scenario: + * validate/tools/launcher/apps/gst-validate.py: + validate: Add a scenario that switches subtitle track while paused + + Integrate it in the launcher + +2014-05-01 11:32:42 +0200 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + validate:launcher: Sensibly simplify scenario handling + +2014-05-01 10:27:53 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate: Plug a minor leak + +2014-04-30 15:51:43 +0200 Thibault Saunier + + * validate/tools/launcher/apps/ges-launch.py: + validate: Use ges-launch recursing path new feature + And fix path to URI conversion + +2014-04-30 15:40:10 +0200 Thibault Saunier + + * validate/tools/launcher/apps/ges-launch.py: + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/baseclasses.py: + * validate/tools/launcher/main.py: + * validate/tools/launcher/utils.py: + validate: Add a gst-validate-launcher documentation + +2014-04-30 11:52:00 +0200 Thibault Saunier + + * validate/tools/launcher/apps/ges-launch.py: + * validate/tools/launcher/baseclasses.py: + * validate/tools/launcher/main.py: + validate:launcher: Port OptionParser to ArgParse + +2014-04-30 11:20:43 +0200 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/main.py: + validate: Can not do reverse playback on sintel sample + + Minor improvement in the CLI + +2014-04-30 11:13:51 +0200 Thibault Saunier + + * validate/tools/launcher/apps/ges-launch.py: + validate:launcher: Do not except meaningless argument in ges-launch + +2014-04-30 11:06:09 +0200 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/baseclasses.py: + validate: Handle per file special scenarios + When a file is int the same folder as a media file and has a name like: + mediafilename.mkv.scenarios_name.scenario we run that scenario on that + particular file + +2014-04-30 09:35:03 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + * validate/tools/gst-validate-transcoding.c: + * validate/tools/gst-validate.c: + validate: Allow specifying scenarios to parse when lisiting them + It used to only handle the scenario present in proper paths, we + also need to handle special scenarios provided by users on the fly + +2014-04-29 20:00:21 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-bin-monitor.c: + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-report.h: + * validate/tools/gst-validate-transcoding.c: + * validate/tools/gst-validate.c: + validate: Handle ERROR on the bus when monitoring the pipeline + This way the user get a clear information in the report about the issue + + sensibly cleanup code + +2014-04-29 19:04:46 +0200 Thibault Saunier + + * validate/data/Makefile.am: + * validate/data/switch_subtitle_track.scenario: + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/baseclasses.py: + validate: Add a scenarios that switchs subtitle track + + Make it easier and cleaner to tell that a switch is actually disabling + a track type. + And run the scenario in gst-validate-launcher by default + +2014-04-29 18:51:54 +0200 Thibault Saunier + + * validate/data/Makefile.am: + * validate/data/switch_audio_track_while_paused.scenario: + * validate/tools/launcher/apps/gst-validate.py: + validate: Add a switch_audio_track_while_paused scenario + And run it as a default + +2014-05-01 18:20:25 +0200 Thibault Saunier + + * validate/tools/gst-validate.c: + validate: Add an action to set an external URI file on playbin at runtime + +2014-05-01 18:19:50 +0200 Thibault Saunier + + * validate/tools/gst-validate.c: + validate: Override switch_track action when using a playbin + And use the playbin feature for that when the pipeline is based on playbin + +2014-05-01 18:17:44 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-utils.c: + validate: Do not g_strrstr with a NULL pointer as needle + +2014-05-01 18:16:16 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate: Print more details when executing the switch_track action + + Fix some issue in the memory freeing codepath of GstValidateAction + +2014-04-29 17:16:50 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-pad-monitor.c: + validate: Do not try to use a NULL iter + +2013-10-01 21:11:35 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-pad-monitor.c: + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-report.h: + pad-monitor: check that no buffers are pushed after a pad is EOS + Make sure no resources are wasted after elements are done with the + current segment + +2014-04-28 13:08:09 +0200 Thibault Saunier + + * validate/tools/launcher/baseclasses.py: + * validate/tools/launcher/main.py: + * validate/tools/launcher/utils.py: + launcher: Now using git annex to handle media files + +2014-04-26 09:52:37 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + validate: Expose a seeking method so other actions types can seek + Other action types might need to seek and we GstValidateScenario need + to know about it, add a method others can use to do the seeking + +2014-04-26 09:16:26 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + * validate/tools/launcher/baseclasses.py: + * validate/tools/launcher/reporters.py: + Revert "validate:launcher: Always put gst-validate result as stderr in reports" + This reverts commit 925ff7542b69bb5516b6eb5b4488da23124a0cbc. + Actually jenkins never truncates on failure stacktrace... we do not + want to set gst-validate as failure stacktrace in our results. That + commit was not usefull. + +2014-04-26 08:11:20 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + * validate/tools/launcher/baseclasses.py: + * validate/tools/launcher/reporters.py: + validate:launcher: Always put gst-validate result as stderr in reports + This way jenkins will always keep the information in its database even + if the test passes + +2014-04-25 18:33:33 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate: Pass -scenario.c into gst-indent and fix some docs + +2014-04-25 18:27:30 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate: Add a "dot-pipeline" action + +2014-04-25 18:26:50 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-utils.c: + * validate/gst/validate/gst-validate-utils.h: + validate: Return a boolean when parsing an enum string + +2014-04-25 13:42:03 +0200 Thibault Saunier + + * validate/data/adaptive_video_framerate.scenario: + * validate/data/adaptive_video_framerate_size.scenario: + * validate/data/adaptive_video_size.scenario: + * validate/data/force_key_unit.scenario: + * validate/data/pause_resume.scenario: + * validate/data/play_15s.scenario: + * validate/data/scrub_forward_seeking.scenario: + * validate/data/seek_backward.scenario: + * validate/data/seek_forward.scenario: + * validate/data/switch_audio_track.scenario: + validate:scenarios: Prefer stop action instead of EOS when appropriate + +2014-04-25 13:19:19 +0200 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/baseclasses.py: + validate:launcher: Properly set error message when sending EOS did not work + +2014-04-25 13:18:41 +0200 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/main.py: + validate:launcher: Add a way to specify tests filtering only on defaults + +2014-04-25 13:17:39 +0200 Thibault Saunier + + * validate/data/switch_audio_track.scenario: + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/baseclasses.py: + validate:launcher: Add support for audio track switching scenario + +2014-04-25 11:32:04 +0200 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/baseclasses.py: + validate:launcher: Make it possible to run any scenario test in gst-validate + +2014-04-25 11:31:27 +0200 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + validate:launcher: Do not run reverse playback on mpegts files + +2014-04-25 11:31:01 +0200 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + validate:launcher: Cleanup media descriptor usage + +2014-04-25 10:23:21 +0200 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/main.py: + validate:launch: Port to the new media_info format + +2014-04-24 15:41:50 +0200 Thibault Saunier + + * validate/gst/validate/Makefile.am: + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-report.h: + * validate/gst/validate/gst-validate-reporter.c: + * validate/gst/validate/media-descriptor-parser.c: + * validate/gst/validate/media-descriptor-parser.h: + * validate/gst/validate/media-descriptor-writer.c: + * validate/gst/validate/media-descriptor-writer.h: + * validate/gst/validate/media-descriptor.c: + * validate/gst/validate/media-descriptor.h: + * validate/tools/gst-validate-media-check.c: + validate: Add a media-descriptor parser and writer + +2014-04-23 13:25:44 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-scenario.c: + validate: Minor printing cleanup + +2014-04-23 13:24:23 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-report.h: + * validate/gst/validate/gst-validate-scenario.c: + * validate/tools/gst-validate-transcoding.c: + * validate/tools/gst-validate.c: + validate: Add a 'stop' action to stop a pipeline + It uses the GST_MESSAGE_REQUEST state with the scenario as a source + so that application can stop running when they receive it on the bus. + +2014-04-23 11:47:10 +0200 Thibault Saunier + + * validate/tools/launcher/baseclasses.py: + * validate/tools/launcher/reporters.py: + * validate/tools/launcher/utils.py: + validate:launcher: Use the new validatelog file + Making the output cleaner and clearer in junit XML file + +2014-04-23 11:27:41 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-bin-monitor.c: + * validate/gst/validate/gst-validate-bin-monitor.h: + * validate/gst/validate/gst-validate-report.c: + * validate/tools/gst-validate-transcoding.c: + * validate/tools/gst-validate.c: + validate: Handle position printing at the monitor level + Instead of replicating that code all around + +2014-04-23 11:16:29 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-report.h: + * validate/gst/validate/gst-validate-runner.c: + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + * validate/gst/validate/validate.c: + * validate/tools/gst-validate-transcoding.c: + validate: Add printing utilities + Allowing the user to print everyting in a file through the + GST_VALIDATE_FILE env variable + +2014-04-22 16:50:08 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate: Add an action to wait for a given amout of time + During that time we will just not execute any new action + + Lower WARNING to DEBUG when no playbcak_time is provided for an + action, it should just be 0. + +2014-04-22 12:02:35 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-pad-monitor.c: + * validate/gst/validate/gst-validate-pad-monitor.h: + validate: Check that for raw, buffers are strictly contained in segment + For encoded data we might need buffers that have timestamp < + segment.start to make sure that we have the keyframe, etc... but for raw + data, buffer end should strictly be inside the segment, be more strict + about that. + +2014-04-22 11:21:34 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-pad-monitor.c: + validate: PAR is not a mandatory field + Also make it possible to check other not mandatory fields in the future + +2014-04-22 11:10:01 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-pad-monitor.c: + validate: Do not use GST_PTR_FORMAT when reporting + It will not work now that we have our own implementation of printf for that in Gst and + thus provide us with pretty useless infos + +2014-04-22 10:49:10 +0200 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/baseclasses.py: + validate:launcher: Send SIGINT signal instead of killing the subprocess + This way we get the result from GstValidate even on timeouts + +2014-04-22 09:42:57 +0200 Thibault Saunier + + * validate/tools/gst-validate.c: + * validate/tools/launcher/apps/gst-validate.py: + validate:launcher: Always set sync=True on fakesink on playback pipelines + This way we are in closer condition of real sink playback. + + some minor cleanup in gst-validate.c + +2014-04-17 12:58:48 +0200 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + validate:launcher: ring-buffer-max-size is in bytes + +2014-04-17 12:17:03 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-report.h: + * validate/gst/validate/gst-validate-reporter.c: + * validate/gst/validate/gst-validate-reporter.h: + * validate/tools/gst-validate-transcoding.c: + * validate/tools/gst-validate.c: + validate: Handle g_log errors at the gst-validate level + +2014-04-17 11:23:23 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate: Allow comments in scenario files + Comment are per line only and start with # + +2014-04-15 15:26:36 +0200 Thibault Saunier + + * validate/tools/launcher/main.py: + validate:launcher: Fix default blacklist management + +2014-04-02 19:14:30 +0200 Thibault Saunier + + * validate/tools/launcher/apps/ges-launch.py: + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/baseclasses.py: + validate:launcher: Print the number of the test being run + +2014-04-02 19:13:50 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-media-info.c: + validate: Avoid segfault when discovering fails + In that case the x->stream_info might not be set + +2014-04-02 12:12:11 +0200 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + validate:launcher: Fix mixup in media_check tests expected file path + +2014-03-31 13:54:27 +0200 Thibault Saunier + + * validate/tools/launcher/utils.py: + validate:launcher: Flush stdout each time we print + So everything gets printed on time on windows and jenkins + +2014-03-31 11:03:48 +0200 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + validate: launcher: Use the ConfigPraser object everywhere for file_infos + +2014-03-28 15:01:12 +0100 Thibault Saunier + + * validate/tools/launcher/apps/ges-launch.py: + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/baseclasses.py: + validate:launcher: Properly handle missing scenarios on the system + +2014-03-28 15:00:45 +0100 Thibault Saunier + + * validate/tools/launcher/apps/ges-launch.py: + validate:launcher: Handle windows path to construct arguments + +2014-03-28 15:00:01 +0100 Thibault Saunier + + * validate/tools/launcher/apps/ges-launch.py: + * validate/tools/launcher/baseclasses.py: + * validate/tools/launcher/utils.py: + validate:launcher: Handle the fact that win32 apps end with .exe + +2014-03-28 11:30:01 +0100 Thibault Saunier + + * validate/configure.ac: + * validate/gst/validate/Makefile.am: + validate: Do not build LD_PRELOAD related code on windows + And do not forget to link against gst-pbutils + +2014-03-28 10:30:21 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-override-registry.c: + validate: Use GModule to 'dlopen' ovverrides + We want gst-validate to be cross platform so use cross platform tools + +2014-03-26 20:09:12 +0100 Thibault Saunier + + * validate/tools/launcher/baseclasses.py: + * validate/tools/launcher/utils.py: + validate:launcher: Put gst logs in a specific file + + Make default timeout 30seconds just in case. + +2014-03-26 19:37:44 +0100 Thibault Saunier + + * validate/tools/launcher/baseclasses.py: + * validate/tools/launcher/main.py: + validate: launcher: Let the use debug on test fail + When a test timeouts, let the user know about the subprocess etc, + and let him possibly connect gdb to it. + +2014-03-26 11:46:48 +0100 Thibault Saunier + + * validate/tools/launcher/apps/ges-launch.py: + validate:launcher: Do not set sample path to letter in ges-launch + +2014-03-26 11:00:32 +0100 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + validate:launcher: Start the server only when actually needed to run filtered tests + +2014-03-26 10:56:58 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-pad-monitor.c: + validate: Do not query pad caps to check if caps are properly fowarded + Query caps will actually get the caps from downstream and those caps + might be different in case there is a Filter in between. What we want is + to check that the caps set on the internally linked pads are correct. + +2014-03-19 18:42:37 +0100 Thibault Saunier + + * validate/tools/launcher/main.py: + launcher: Allow user to set media-files directory + That was broken by 71dee6c3843d02d9d41bbb353cb3fa653190018d + +2014-03-19 17:43:43 +0100 Thibault Saunier + + * validate/tools/gst-validate.c: + tools:validate: Start printing position on ASYNC_DONE + As this is what is done in the scenarios. + +2014-03-19 18:09:09 +0100 Edward Hervey + + * validate/tools/launcher/main.py: + launcher: Don't hardcode option defaults + Since they are relative to other options, we need to post-process them + to get the proper value. + Fixes using the launcher with non-default MAIN_DIR + +2014-03-19 17:13:14 +0100 Edward Hervey + + * validate/tools/launcher/main.py: + launcher: Warn if MAIN_DIR isn't present + And move blacklist file listing to further down + +2014-03-19 17:04:14 +0100 Edward Hervey + + * validate/tools/launcher/main.py: + launcher: No need to start a web server when listing tests + It's not needed and makes listing faster. + Also sort the list of tests + +2014-03-19 17:03:05 +0100 Edward Hervey + + * validate/tools/launcher/main.py: + launcher: --sync: Only update/clone git repo if specified + Allows: + * handling non-git-based asset directory + * working offline + * working without forcing updates + +2014-03-19 17:02:03 +0100 Edward Hervey + + * validate/gst/validate/gst-validate-scenario.c: + validate-scenario: Handle non-set env variable + Nothing guarantees it's present/set + +2014-03-12 15:23:33 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate:scenario: Do not be strict about position after not accurate seek + +2014-03-12 14:24:02 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate:scenario: Cleanup output and pass into gst-indent + +2014-03-12 12:21:38 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate:scenario: Cleanup output of --list-scenarios + +2014-03-12 12:04:52 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate: scenario: Load scenario if the name is actually a path to a file + +2014-02-12 11:20:06 +0100 Thibault Saunier + + * validate/tools/launcher/main.py: + validate: tools: Fix path to media folder + +2014-02-19 13:07:03 +0100 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/baseclasses.py: + validate:tools: Clean test between runs when running forever + +2014-02-19 10:31:15 +0100 Thibault Saunier + + * validate/tools/launcher/baseclasses.py: + validate: Do not check result furthers if alredy set as passing + +2014-02-19 09:58:22 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate:scenario: Pass into gst-indent + +2014-02-19 09:56:12 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate: Add actions to the actions list only when they are fully parsed + Otherwize in some corner cases they can be executed before they are actually parsed + +2014-01-24 17:36:53 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate: Ignore EOS actions that can not be executed + +2014-02-18 18:49:00 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate: Handle various paths in GST_VALIDATE_SCENARIOS_PATH + +2014-02-18 18:15:33 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + validate:scenario: Make GstValidateAction a GstMiniObject + +2014-02-18 18:13:39 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-utils.c: + * validate/gst/validate/gst-validate-utils.h: + validate: Move enums and flags deserialization from scenario to utilities + This way it can be reused. + +2014-02-18 18:09:37 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + validate:scenario: Make the pipeline puiblic + This way people can access it from outside the main action implementation. + +2014-02-14 16:07:51 +0100 Thibault Saunier + + * validate/tools/launcher/apps/ges-launch.py: + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/baseclasses.py: + validate:launcher: Avoid running useless tests + For example we should not check if duration are equal when transcoding + with scenario set. + Also checking if position is in the seeked segment should be done at + a lower level + +2014-02-13 15:35:01 +0100 Thibault Saunier + + * validate/tools/launcher/baseclasses.py: + validate:launcher: Avoid seeking in output files to parse them + Tihs creates issue and missing content. + +2014-02-13 15:34:10 +0100 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + validate:launcher: Properly classify test for media check + +2014-02-13 15:33:25 +0100 Thibault Saunier + + * validate/tools/launcher/apps/ges-launch.py: + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/utils.py: + validate:launcher: Handle issue with unknown framerate in HLS while transcoding + +2014-02-13 15:31:58 +0100 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/baseclasses.py: + * validate/tools/launcher/utils.py: + validate:tools: Handle cases were EOS does not stop the pipeline in the launcher + + Fix parsing of GstClockTime + + Avoid using play_15s scenario when not necessary + +2014-02-12 11:18:14 +0100 Thibault Saunier + + * validate/tools/launcher/apps/ges-launch.py: + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/baseclasses.py: + * validate/tools/launcher/main.py: + * validate/tools/launcher/utils.py: + validate: tools: Use the new scenario discovering fearure in the launcher + +2014-02-12 00:28:41 +0100 Thibault Saunier + + * validate/data/adaptive_video_framerate.scenario: + * validate/data/adaptive_video_framerate_size.scenario: + * validate/data/adaptive_video_size.scenario: + * validate/data/alternate_fast_backward_forward.scenario: + * validate/data/fast_backward.scenario: + * validate/data/fast_forward.scenario: + * validate/data/force_key_unit.scenario: + * validate/data/pause_resume.scenario: + * validate/data/play_15s.scenario: + * validate/data/reverse_playback.scenario: + * validate/data/scrub_forward_seeking.scenario: + * validate/data/seek_backward.scenario: + * validate/data/seek_forward.scenario: + * validate/data/seek_forward_backward.scenario: + * validate/data/seek_with_stop.scenario: + * validate/data/simple_seeks.scenario: + * validate/data/switch_audio_track.scenario: + * validate/data/update_start.scenario: + * validate/data/update_stop.scenario: + validate: Update all scenario to use the new description feature + + Fix minor issues in scenario files + +2014-02-12 00:28:18 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + * validate/tools/gst-validate-transcoding.c: + * validate/tools/gst-validate.c: + validate: Add a way to save details about avalaible scenarios in a file + +2014-02-11 23:05:00 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + * validate/tools/gst-validate.c: + validate: Add a way to add a "description" to scenario files + Print details about the descriptions when listing scenario in a KeyFile + format + The description can contain any information about the scenario such as its duration before + EOS, how long the pipeline needs to be so the scenario can be applied...etc + +2014-02-11 23:09:57 +0100 Thibault Saunier + + * validate/tools/gst-validate-transcoding.c: + validate: tools: Init gst-validate before listing scenarios + And return 0 when only listing scenarios + +2014-02-10 16:48:44 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + validate:scenario: Handle backslashes in scenario files + +2014-02-06 17:24:30 +0100 Thibault Saunier + + * validate/tools/launcher/baseclasses.py: + * validate/tools/launcher/main.py: + * validate/tools/launcher/utils.py: + validate:tools: Rework the way we handle options + Make groups so it is easier for users to find what they look for + By default have 1 single directory where everything is oututed + (main-dir) + Add a way to specify how and where to look for remote assets + +2014-02-06 17:23:10 +0100 Thibault Saunier + + * validate/tools/launcher/baseclasses.py: + validate:tools:launcher: Take into account the position value when rendering + When rendering a files we try to use the size of the outputed file to + determine wether we are timeout or not, but if that fails + try to check the position + +2014-02-06 17:22:36 +0100 Thibault Saunier + + * validate/tools/launcher/apps/ges-launch.py: + * validate/tools/launcher/apps/gst-validate.py: + validate: Better organize rendered files + +2014-01-31 12:21:21 +0100 Thibault Saunier + + * validate/tools/launcher/baseclasses.py: + * validate/tools/launcher/utils.py: + validate:tools: Use regex for parsing when appropriate + +2014-01-31 00:23:29 +0100 Thibault Saunier + + * validate/tools/launcher/main.py: + validate:toold: Add a --output-dir parametter + +2014-01-31 00:22:57 +0100 Thibault Saunier + + * validate/tools/launcher/apps/ges-launch.py: + * validate/tools/launcher/apps/gst-validate.py: + validate:tools: Keep file extension in test classnames + + add test "namespace" in transcoded files + +2014-01-30 16:59:21 +0100 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + validate:tools: Add a class to back pipeline creation in gst-validate + +2014-01-30 16:58:58 +0100 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/utils.py: + validate:tools: Define supported protocols in an enum + +2014-01-30 16:56:51 +0100 Thibault Saunier + + * validate/tools/launcher/baseclasses.py: + validate: toold: Properly define scenario properties + +2014-01-30 16:38:37 +0100 Thibault Saunier + + * validate/data/Makefile.am: + * validate/data/reverse_playback.scenario: + * validate/data/simple_backward.scenario: + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/baseclasses.py: + validate: Rename simple_backward to reverse_playback as this is what it does + +2014-01-30 15:40:21 +0100 Thibault Saunier + + * validate/tools/launcher/apps/ges-launch.py: + * validate/tools/launcher/apps/gst-validate.py: + validate:tools: Add namespace in apps global variables + Avoiding conflicts + +2014-01-30 13:36:04 +0100 Thibault Saunier + + * validate/tools/launcher/main.py: + validate:tools: Allow user to append paths to medias + +2014-01-30 13:25:57 +0100 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/baseclasses.py: + validate:tools: Make use of the new seek_with_stop scenario + +2014-01-30 12:42:25 +0100 Thibault Saunier + + * validate/tools/launcher/apps/ges-launch.py: + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/baseclasses.py: + * validate/tools/launcher/utils.py: + validate:tools: Implement the logic of validate ouput parsing in the baseclass + + Add some logic to check that we are mot playing outside wanted segment + +2014-01-30 12:20:33 +0100 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/baseclasses.py: + * validate/tools/launcher/main.py: + tools:validate: Make default blacklist handled by managers themselves + +2014-01-30 11:59:54 +0100 Thibault Saunier + + * validate/data/Makefile.am: + * validate/data/scrub_forward_seeking.scenario: + * validate/data/seek_backward.scenario: + * validate/data/seek_forward.scenario: + * validate/data/seek_with_stop.scenario: + validate: data: Avoid using seek.stop time when not necessary + Instead send an EOS. + And add a seek_with_stop scenario to test that particular feature + +2014-01-29 17:39:14 +0100 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + validate:tools: Only discover files with media-check + +2014-01-29 17:37:57 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-media-info.c: + * validate/gst/validate/gst-validate-media-info.h: + * validate/tools/gst-validate-media-check.c: + validate:tools: Add a 'discover-only' option to media-check + +2014-01-27 12:20:02 +0100 Thibault Saunier + + * validate/data/seek_forward.scenario: + data: Let playback until the end on last seek of seek_forward if duration < 30s + +2014-01-24 16:38:12 +0100 Thibault Saunier + + * validate/tools/launcher/baseclasses.py: + * validate/tools/launcher/main.py: + validate: tools: Add a --fatal-error option to the launcher + +2014-01-24 13:59:56 +0100 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/baseclasses.py: + validate: tools: Implement the notion of hard timeout + Allowing to define timeout that is not relative to the last observed number. + +2014-01-24 11:41:25 +0100 Thibault Saunier + + * validate/tools/launcher/apps/ges-launch.py: + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/baseclasses.py: + validate: tools: Create a class for scenarios + +2014-01-24 11:31:42 +0100 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + validate: tools: Change timeouts depending on used protocol + +2014-01-24 11:29:50 +0100 Thibault Saunier + + * validate/tools/gst-validate-transcoding.c: + * validate/tools/gst-validate.c: + * validate/tools/launcher/utils.py: + validate:tools: Implement Buffering support in the various tools + +2014-01-23 00:15:54 +0100 Thibault Saunier + + * validate/tools/launcher/apps/ges-launch.py: + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/baseclasses.py: + * validate/tools/launcher/main.py: + validate:tools: Blacklist some scenario/protocol combinations + And add the option for user to easilly blacklist tests + +2014-01-22 23:25:09 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-reporter.c: + validate: Plug a leak in validate-reporter + +2014-01-22 23:22:59 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-pad-monitor.c: + * validate/gst/validate/gst-validate-reporter.c: + validate:tools: Do not forget to give a ref for reporter's reports + Also enhance a bit report 'wording' + +2014-01-15 16:11:39 +0100 Thibault Saunier + + * validate/tools/launcher/baseclasses.py: + * validate/tools/launcher/main.py: + * validate/tools/launcher/utils.py: + validate:tools: Print test result in the terminal after the end of each test + +2014-01-15 16:07:26 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + scenario: Enhance explanation about seek execution failure + +2014-01-14 18:07:46 +0100 Thibault Saunier + + * validate/tools/launcher/baseclasses.py: + * validate/tools/launcher/main.py: + validate:tools: Add an option to run testforever + +2014-01-14 18:05:45 +0100 Thibault Saunier + + * validate/tools/launcher/utils.py: + validate: tools: Cleanup the way we return code in position query + +2014-01-14 10:32:53 +0100 Thibault Saunier + + * validate/tools/launcher/apps/ges-launch.py: + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/baseclasses.py: + validate:tools: Use the same semantic for all tests classnames + +2014-01-14 10:31:27 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + scenario: Do not remove GSource if it has never been set + +2014-01-14 10:28:01 +0100 Thibault Saunier + + * validate/data/play_15s.scenario: + data: Add a scenario where we send EOS after 15secs if the duration is > to that + +2014-01-13 17:31:57 +0100 Thibault Saunier + + * validate/tools/launcher/Makefile.am: + * validate/tools/launcher/RangeHTTPServer.py: + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/baseclasses.py: + * validate/tools/launcher/httpserver.py: + * validate/tools/launcher/main.py: + validate:tools: Add support for testing http streams locally + +2014-01-13 09:47:45 +0100 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + validate:tools: Some cleanup in gst-validate test launcher + +2014-01-13 11:13:02 +0100 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + validate:tools: Add actuall tests for media checking + +2014-01-13 11:07:43 +0100 Thibault Saunier + + * validate/tools/gst-validate-media-check.c: + validate: tools: media-check: When comparing with a file just compare + We do not want to know if the file is seekable etc, but in that case we + want to see that the results are stable throughout the various runs + Also make sure to report an understandable error if the media file info + could not be parsed + +2014-01-13 09:32:14 +0100 Thibault Saunier + + * validate/tools/gst-validate-transcoding.c: + * validate/tools/gst-validate.c: + validate: tools: Do not dot the pipeline every 50ms, it is a bit exessive + +2014-01-10 18:00:27 +0100 Thibault Saunier + + * validate/tools/gst-validate-transcoding.c: + * validate/tools/gst-validate.c: + validate:tools: Return an exit code != 0 if pipeline can't go to playing + And give some information to the user about why the return code is !=0 + everywhere it happens + +2014-01-10 17:21:44 +0100 Thibault Saunier + + * validate/tools/launcher/baseclasses.py: + validate:tools: Add informations about the test in the log files + +2014-01-10 16:56:44 +0100 Thibault Saunier + + * validate/tools/launcher/baseclasses.py: + validate:tools: Remove reference to get_backtrace which is not implemented + + Enhance Message about launched apps + +2014-01-10 16:46:00 +0100 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/baseclasses.py: + * validate/tools/launcher/reporters.py: + validate:tools: Do not duplicated name in the classname in xunit reports + +2014-01-10 15:31:01 +0100 Thibault Saunier + + * validate/tools/launcher/utils.py: + validate:tools: Do not check if position > duration + This is actually done by the scenario themselve. Instead if it is the + case, we return 0, this way it will timeout if it happens too many times + concecutively + +2014-01-10 15:30:38 +0100 Thibault Saunier + + * validate/tools/launcher/baseclasses.py: + * validate/tools/launcher/reporters.py: + * validate/tools/launcher/utils.py: + validate:tools: Properly inform the user about the log location when test fails + +2014-01-10 15:29:31 +0100 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/main.py: + validate:tools: Add an option to generate .media_info files + So we can properly choose what media should be tested only placing + media_file as needed. + +2014-01-10 15:27:46 +0100 Thibault Saunier + + * validate/tools/launcher/apps/ges-launch.py: + * validate/tools/launcher/apps/gst-validate.py: + validate:tools: use more scenarios in gst-validate launcher + And ensure that the list does not get mixed up with as we are sharing + "symboles" between all the files + +2014-01-10 15:26:29 +0100 Thibault Saunier + + * validate/data/simple_backward.scenario: + validate:tools: Play the entire file in simple_backward + +2014-01-10 14:31:24 +0100 Thibault Saunier + + * validate/data/fast_forward.scenario: + validate:tools: Fix the fast forward scenario to handle any file duration + +2014-01-10 12:41:30 +0100 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + validate:tools: Do not try to transcode images + +2014-01-10 12:01:43 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-media-info.c: + * validate/gst/validate/gst-validate-media-info.h: + validate: Properly handle images in the media-info helper + In the case of images we should not check reverse playback, fast + forward etc... + We also should keep the information + +2014-01-10 11:36:10 +0100 Thibault Saunier + + * validate/configure.ac: + * validate/tools/launcher/loggable.py: + * validate/tools/launcher/main.py: + validate:tools: Minor cleanups + +2014-01-10 11:35:47 +0100 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + validate:tools: Fix classname in gst-launch transcoding tests + +2014-01-10 11:11:10 +0100 Thibault Saunier + + * validate/tools/launcher/reporters.py: + validate:tools: Properly name the project launcher in the report + +2014-01-10 10:58:54 +0100 Thibault Saunier + + * validate/tools/launcher/main.py: + validate:tools: Create the rendering directory if it does not exist + +2014-01-10 10:27:25 +0100 Thibault Saunier + + * validate/tools/launcher/baseclasses.py: + * validate/tools/launcher/main.py: + * validate/tools/launcher/utils.py: + validate:tools: Add an option to desativate ANSI colors + And enhance some debugging output + +2014-01-10 10:12:13 +0100 Thibault Saunier + + * validate/tools/launcher/apps/ges-launch.py: + * validate/tools/launcher/baseclasses.py: + * validate/tools/launcher/utils.py: + validate:tools: Remove our dependency to PyGobject + +2014-01-09 18:43:15 +0100 Thibault Saunier + + * validate/tools/launcher/Makefile.am: + * validate/tools/launcher/apps/Makefile.am: + * validate/tools/launcher/main.py: + validate:tools: Do not forget to add Makefile.am and main.py + +2014-01-09 16:57:54 +0100 Thibault Saunier + + * validate/tools/launcher/apps/ges-launch.py: + * validate/tools/launcher/baseclasses.py: + validate:tools: Enhance the way we detect if ges-launch can be used + We make sure it has been compiled against gst-validate + +2014-01-09 15:24:52 +0100 Thibault Saunier + + * validate/tools/launcher/baseclasses.py: + * validate/tools/launcher/utils.py: + validate:tools: Set video/webm instead of video/x-matroska as caps for webm + + some mirore indentation cleanups + +2014-01-09 15:24:05 +0100 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + validate:tools: Do not forget to keep our ref to file_info g-v-transcode + +2014-01-09 15:23:38 +0100 Thibault Saunier + + * validate/tools/launcher/apps/ges-launch.py: + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/utils.py: + validate:tools: Cleanup how we check result of rendering test + Factor out a method in the utils, and make use of it for both ges-launch and + gst-validate-transcode + +2014-01-09 15:20:46 +0100 Thibault Saunier + + * validate/tools/launcher/apps/gst-validate.py: + validate:tools: Don't give file duration as timeout for gst-validate + We use the other mean letting us actually control the process + advancement. + +2014-01-09 15:17:53 +0100 Thibault Saunier + + * validate/tools/launcher/apps/ges-launch.py: + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/baseclasses.py: + * validate/tools/launcher/utils.py: + validate:tools: Veryfy test manager are operationnal before using them + +2014-01-09 15:15:51 +0100 Thibault Saunier + + * validate/tools/gst-validate-launcher.in: + * validate/tools/launcher/apps/ges-launch.py: + validate:tools: Move the main function in a dedictaed file + +2014-01-09 11:14:19 +0100 Thibault Saunier + + * validate/tools/launcher/reporters.py: + * validate/tools/launcher/utils.py: + validate:tools: Print some statistic at the end of the test run + +2014-01-09 11:13:40 +0100 Thibault Saunier + + * validate/tools/gst-validate.c: + validate:tools: Print position every 50ms in gst-validate + +2014-01-09 09:39:05 +0100 Thibault Saunier + + * validate/configure.ac: + * validate/tools/Makefile.am: + * validate/tools/apps/ges-projects-tests.py: + * validate/tools/apps/gst-validate.py: + * validate/tools/gst-validate-launcher.in: + * validate/tools/gst-validate-launcher.py: + * validate/tools/launcher/__init__.py: + * validate/tools/launcher/apps/ges-launch.py: + * validate/tools/launcher/apps/gst-validate.py: + * validate/tools/launcher/baseclasses.py: + * validate/tools/launcher/loggable.py: + * validate/tools/launcher/reporters.py: + * validate/tools/launcher/utils.py: + * validate/tools/loggable.py: + * validate/tools/reporters.py: + * validate/tools/testdefinitions.py: + * validate/tools/utils.py: + validate:tools: Rename files around and integrate into autotools + File distribution used to be messy, clean it all up. Also make sure the + launcher is integrated into the autotools. + +2014-01-09 09:28:02 +0100 Thibault Saunier + + * validate/tools/testdefinitions.py: + validate: tools: Enhance error message for GstValidate tests + +2014-01-09 09:27:50 +0100 Thibault Saunier + + * validate/tools/testdefinitions.py: + * validate/tools/utils.py: + validate: tools: Concider timeouts as errors when printing tests + +2014-01-09 09:14:27 +0100 Thibault Saunier + + * validate/tools/apps/ges-projects-tests.py: + * validate/tools/apps/gst-validate.py: + * validate/tools/gst-validate-launcher.py: + * validate/tools/testdefinitions.py: + * validate/tools/utils.py: + validate: tools: Refactor and add a GstValidateTranscodeTest class + +2014-01-08 18:51:14 +0100 Thibault Saunier + + * validate/tools/apps/gst-validate.py: + * validate/tools/gst-validate-launcher.py: + * validate/tools/loggable.py: + * validate/tools/reporters.py: + * validate/tools/testdefinitions.py: + validate: launcher: add the debug logger from pitivi + It is way more powerfull, simple to use and usefull + than the stock python one and has been proved to work reliably + +2014-01-13 09:41:16 +0100 Thibault Saunier + + * validate/tools/gst-validate.c: + validate: tools: Unref the pipeline before the runner and monitor + Avoids segfault in some cases, and monitors and runners have week ref on + their targets. + +2014-01-08 09:49:38 +0100 Thibault Saunier + + * validate/tools/apps/gst-validate.py: + validate: tools: Add a gst-validate test manager + +2014-01-08 09:44:02 +0100 Thibault Saunier + + * validate/gst/validate/gst-validate-media-info.c: + validate: tools: media-info: Fixes in the media file descriptor parsing code + We used to always fail when the user was passing something not NULL as err + +2013-12-31 11:45:07 +0100 Thibault Saunier + + * validate/tools/apps/ges-projects-tests.py: + * validate/tools/gst-validate-launcher.py: + * validate/tools/reporters.py: + * validate/tools/testdefinitions.py: + * validate/tools/utils.py: + validate: tools: Cleanup test launcher tool + Previous commit was not meant to be pushed and those two should have + been fixed up together, sorry for the mistake + +2014-01-30 15:52:34 -0300 Reynaldo H. Verdejo Pinochet + + * validate/gst/validate/Makefile.am: + validate: fix parallel build + Without this, parallel building with > 2 jobs fails. + Also, LDFLAGS should not contain -l flags but _LIBADD. + +2014-01-30 15:47:15 -0300 Reynaldo H. Verdejo Pinochet + + * validate/gst/validate/gst-validate-default-overrides.c: + validate: drop unneeded stdio include + +2013-12-31 11:45:07 +0100 Thibault Saunier + + * validate/tools/apps/ges-projects-tests.py: + * validate/tools/gst-validate-launcher.py: + * validate/tools/reporters.py: + * validate/tools/testdefinitions.py: + * validate/tools/utils.py: + Add a test launcher tool + +2013-11-25 21:51:11 +0100 Lubosz Sarnecki + + * validate/gst/validate/Makefile.am: + * validate/pkgconfig/gst-validate.pc.in: + validate: fix installation + * install headers + * fix libname in pk file + +2013-11-15 05:22:24 -0500 Vincent Penquerc'h + + * validate/gst/validate/gst-validate-scenario.c: + validate-scenarios: list scenarios in GST_VALIDATE_SCENARIOS_PATH + GST_VALIDATE_SCENARIOS_PATH was used only for loading scenarios, + so any in that path would not be listed by -l. + Change-Id: If3cb94867ef3876933bda02477675c8ccf67baaf + +2013-10-18 16:22:03 -0300 Thibault Saunier + + * validate/tools/gst-validate-transcoding.c: + tools: transcoding: Avoid reencoding unless explicitely specified + +2013-10-28 19:49:52 -0300 Thibault Saunier + + * validate/gst/validate/gst-validate-pad-monitor.c: + pad-monitor: Do not concider TIME_NONE as 0 for serialized events + In case we have serialized events right after a buffer that had no + timestamp set we concider that last timestamp was 0, but we can + actually not concider the timestamp at all in that case as it is + only "meaningless value". + +2013-10-19 13:41:01 -0300 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + * validate/tools/gst-validate-transcoding.c: + * validate/tools/gst-validate.c: + tools: Simplify the setting of action scenario vs config scenario + This make it easier for user to understand the difference between + the two concepts and avoids confusion. + Change-Id: Ib42913722c93a1e7e3c8b156173c458230946592 + Conflicts: + validate/tools/gst-validate-transcoding.c + validate/tools/gst-validate.c + +2013-10-25 11:33:54 +0200 Thibault Saunier + + * validate/tools/gst-validate.c: + scenario: Do not execute anything when listing scenarios + +2013-10-25 11:31:58 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + scenario: Add a "set-feature-rank" config action + This action can be used to change the rank of a particular element, + so you can force a particular element to be used when using + autoplugging elements (such as decodebin, encodebin, and friends) + +2013-10-25 11:29:04 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-bin-monitor.c: + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + * validate/tools/gst-validate-transcoding.c: + scenario: Add support for "config" actions, actions executed at parse time + This type of actions is used to change some parametter on GStreamer + core and it plugins, it can be fore example, to change the rank of a + plugin or things like that. + +2013-10-16 17:35:36 -0300 Thibault Saunier + + * validate/tools/gst-validate-transcoding.c: + validate-transcoding: Dot pipeline on error + +2013-10-26 03:01:37 -0700 Zaheer Abbas Merali + + * vagrant/Vagrantfile: + * vagrant/ansible_hosts: + * vagrant/gst-streaming-server-git.yml: + * vagrant/gstreamer-git.yml: + * vagrant/gstreamer.yml: + * vagrant/ipython.yml: + * vagrant/playbook.yml: + vagrant: initial commit + Vagrant environment to do GStreamer development, debugging and + testing. + +2013-10-21 13:06:46 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + mi-info: handle streams with unknown duration + +2013-10-21 09:08:18 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + mi-info: start handling missing plugin messages + We only print them to the debug log for now. + +2013-10-21 09:07:09 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + * mediainfo/src/mi-preview.vala: + mi-preview: use ensure_native() in realized() + This fixes X crashers at startup when preparing the overlay. + +2013-10-19 21:15:08 +0200 Stefan Sauer + + * mediainfo/TODO: + mi/TODO: planning update + +2013-10-19 21:13:46 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + mi-info: go back to use the sync api + If we discover 'too quickly' the machinery seems to get into a state, where it + does not discover anything anymore. + +2013-10-18 23:33:50 +0200 Stefan Sauer + + * mediainfo/src/mi-app.vala: + * mediainfo/src/mi-info.vala: + * mediainfo/src/mi-preview.vala: + mi-preview: reflow the overlay sync + We need to listen to preview-widget resizing to send an expose to the gst- + overlay. Defer discovering until the ui has be realized. + +2013-10-18 18:22:33 +0200 Stefan Sauer + + * mediainfo/src/mi-app.vala: + mi-app: use an idle-handler to set the initial directory + This ensures we don't emit selection changed signals before we're up and running. + +2013-10-17 22:34:25 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + mi-info: improve media preview + Set double_buffering when we analyzed the media. Drop signal handlers on preview + widget for delayed configuration. Prepare preview as soon as we have discovered. + +2013-10-14 11:25:39 -0300 Thibault Saunier + + * validate/configure.ac: + * validate/tools/Makefile.am: + Properly link against gstreamer-video as it is now needed + +2013-10-14 11:20:03 -0300 Thibault Saunier + + * validate/gst/validate/gst-validate-media-info.c: + media-info: Do not use GST_PTR_FORMAT with g_print + Fix compilation + +2013-10-14 11:07:03 -0300 Thibault Saunier + + * validate/data/scrub_forward_seeking.scenario: + data: Avoid races in the scrub_forward seeking scenario + Make sure that it does not last too long if the file is long (scrubing + on 10 secs maximum), and make sure that we do not end up seeking after + the max duration + +2013-10-14 11:05:48 -0300 Thibault Saunier + + * validate/gst/validate/gst-validate-report.c: + report: Set refcount=1 when creating a report + As it should start with 1 reference, not 0 + +2013-10-09 09:35:29 -0300 Thibault Saunier + + * validate/data/Makefile.am: + * validate/data/force_key_unit.scenario: + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-report.h: + * validate/tools/gst-validate-transcoding.c: + scenario: Add an action that checks the "force-key-unit" event execution + +2013-10-09 09:33:06 -0300 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + scenario: Make the get_clocktime helper a public method + So it can be reused outside of the core code + +2013-10-07 19:47:15 -0300 Thibault Saunier + + * validate/gst/validate/gst-validate-pad-monitor.c: + pad-monitor: Do not try to compare 2 not fixed values + There is no reliable way of checking those values in the case they + are not fixed, let's just make sure we get fixed values before + executing the check + +2013-10-07 17:40:54 -0300 Thibault Saunier + + * validate/data/Makefile.am: + * validate/data/adaptive_video_framerate_size.scenario: + data: Add an adaptive video framerate and size scenario + +2013-10-07 17:18:37 -0300 Thibault Saunier + + * validate/data/Makefile.am: + * validate/data/adaptive_video_framerate.scenario: + * validate/tools/gst-validate-transcoding.c: + data: Add an adaptive video framerate scenario + +2013-10-07 12:08:28 -0300 Thibault Saunier + + * validate/data/Makefile.am: + * validate/data/adaptive_video_size.scenario: + data: Add a scenario where we change the video size on during playback + +2013-10-07 12:06:22 -0300 Thibault Saunier + + * validate/tools/gst-validate-transcoding.c: + transcoding: Add a new action to change restriction caps at runtime + +2013-10-07 12:07:47 -0300 Thibault Saunier + + * validate/tools/gst-validate-transcoding.c: + transcoding: Fix the way we get pad caps + +2013-10-07 10:59:39 +0200 Stefan Sauer + + * mediainfo/TODO: + * mediainfo/src/mi-info.vala: + mi-todo: planning and todo comment update + +2013-10-07 10:59:15 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + mi-info: add two more wikilinks + +2013-10-07 10:07:31 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + mi-info: filter language-code from tags + We already show the language code as a separate field. + +2013-10-05 13:29:52 -0300 Thibault Saunier + + * validate/data/alternate_fast_backward_forward.scenario: + * validate/data/fast_backward.scenario: + * validate/data/fast_forward.scenario: + * validate/data/seek_backward.scenario: + * validate/data/seek_forward.scenario: + * validate/data/seek_forward_backward.scenario: + * validate/data/simple_backward.scenario: + * validate/data/simple_seeks.scenario: + * validate/data/update_start.scenario: + * validate/data/update_stop.scenario: + data: Set seeks to accurate+flush by default + +2013-10-05 12:44:39 -0300 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + scenario: Pass through gst-indent + +2013-10-05 12:43:27 -0300 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + scenario: Use g_error instead of exit (0) + +2013-10-05 12:43:03 -0300 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + scenario: Factor out function to get GstClockTime out of a structure + +2013-10-05 12:01:46 -0300 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + scenario: Use a weak ref to the pipeline + We are listening to it, we should not be owning a ref to it. + +2013-10-05 12:00:35 -0300 Thibault Saunier + + * validate/docs/validate-usage.txt: + * validate/gst/validate/gst-validate-runner.c: + * validate/tools/gst-validate-transcoding.c: + runner: Use "18" as exit code in case of error + It is a random number, but it will in most cases give people a hint + that gst-validate reported a critical issue, and thus set the return + code, only by looking at it + Also make use of gst_validate_runner_print() in + gst-validate-transcoding.c as we were copy pasting that method there. + +2013-10-03 19:23:57 -0300 Thibault Saunier + + * validate/data/seek_backward.scenario: + * validate/data/seek_forward.scenario: + * validate/gst/validate/gst-validate-scenario.c: + scenario: Handle formulas in playback_time + And port seek forward/backward scenarios to relative seeking + +2013-09-28 02:18:55 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-report.h: + * validate/gst/validate/gst-validate-scenario.c: + scenario: Check that all action were properly executed + +2013-09-28 00:15:13 +0200 Thibault Saunier + + * validate/data/Makefile.am: + * validate/data/scrub_forward_seeking.scenario: + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + scenario: Add the notion of repeated actions + +2013-09-28 00:05:51 +0200 Thibault Saunier + + * validate/gst/validate/Makefile.am: + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-utils.c: + * validate/gst/validate/gst-validate-utils.h: + utils: Add util functions to parse simple mathematical expressions + And make use of it to set the start of a seek + +2013-09-28 00:12:07 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + scenario: Error out and exit when we fail loading a scenario + +2013-10-04 09:58:17 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + mi-info: add more wikilinks + +2013-10-04 07:51:46 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + mi-info: pretty print framerates + Avoid to print 0 fps. Handle the special 0/1 case for still images. + +2013-10-03 18:14:18 -0400 Olivier Crête + + * validate/gst/validate/gst-validate-scenario.h: + gst-validate-scenario: Only typedef the struct once + Some gcc versions don't like the typedef being done twice + +2013-10-03 22:23:22 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + mi-info: add a helper to format bit-rates + Print bit-rates in kbit/sec. Add handling for unknown values and ranges. + +2013-10-03 22:22:46 +0200 Stefan Sauer + + * mediainfo/src/mi-preview.vala: + mi-preview: ensure that natural-size >= min-size + +2013-10-01 08:21:45 +0200 Stefan Sauer + + * mediainfo/TODO: + mi/TODO: planning update + +2013-10-01 07:48:20 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + mi-info: reset toc info when we did not get disco info + +2013-10-03 05:32:54 -0400 Vincent Penquerc'h + + * validate/gst/validate/gst-validate-scenario.c: + scenario: do not set default seek flags + Seeks will be done with no particular flags, unless specified + in the scenario. + +2013-09-30 15:39:54 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + mi-info: filter buffer entries from caps + Filter buffer entries from caps before showing them as string. + +2013-09-28 07:19:59 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + mi-info: show tooltip for caps labels with full caps string + +2013-09-28 07:12:27 +0200 Stefan Sauer + + * mediainfo/TODO: + * mediainfo/src/mi-info.vala: + mi-info: send seek events when clicking toc entries + Get the start-pos from the active toc entry and seek. + +2013-09-27 08:03:59 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + mi-info: use a TreeView for the toc + Use a TreeView with a TreeStore to show toc-entries. + +2013-09-30 09:51:21 -0400 Olivier Crête + + * validate/tools/gst-validate.c: + gst-validate: Don't use the GOptionContext after freeing it + +2013-09-25 08:19:26 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + mi-info: indent toc entries + Also add todo for how to make it a treeview instead + +2013-09-25 08:01:29 +0200 Stefan Sauer + + * mediainfo/TODO: + TODO: planning + +2013-09-25 07:58:49 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + mi-info: mark nullable parameters as such + +2013-09-25 07:54:43 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + mi-info: add start/stop times in toc + +2013-09-21 00:23:17 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-pad-monitor.c: + pad-monitor: Check if channel-mask is present only if channels > 2 + As it is not a mandatory field otherwize + https://bugzilla.gnome.org/show_bug.cgi?id=708499 + +2013-09-19 07:38:20 -0300 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + scenario: Add GST_VALIDATE_SCENARIOS_PATH environment variable + So you can specify the PATHS where to look for scenario files + +2013-09-16 10:03:07 -0300 Thiago Santos + + * validate/data/update_start.scenario: + * validate/data/update_stop.scenario: + scenarios: add 2 new scenarios for seeks with different seek types + They test seeks that only update the stop or the start position, some + demuxers seem not to handle the case where start type is set to None. + +2013-09-13 12:09:30 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-report.h: + * validate/gst/validate/gst-validate-scenario.c: + validate-scenario: track position query results closer + Always keep probing the pipeline for the current position and compare + with the latest requested seek segment to detect if the seek boundaries + are being respected + +2013-09-17 15:56:19 -0300 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + scenario: Make it possible to define mandatory fields + And give a descrpition for actions + +2013-09-02 11:11:15 -0400 Vincent Penquerc'h + + * validate/data/Makefile.am: + * validate/data/switch_audio_track.scenario: + * validate/gst/validate/gst-validate-scenario.c: + scenario: add a track switch command, and an audio track switch test + The "switch-track" command can be used to switch tracks. The "type" + argument selects which track type to change (can be "audio", "video", + or "text"). The "index" argument selects which track of this type + to use: it can be either a number, which will be the Nth track of + the given type, or a number with a "+" or "-" prefix, which means + a relative change (eg, "+1" means "next track", "-1" means "previous + track"). + Conflicts: + validate/gst/validate/gst-validate-scenario.c + +2013-09-16 18:48:38 -0300 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + scenario: Make it possible to register action parsing funcs before init + +2013-09-13 15:48:56 -0300 Thibault Saunier + + * validate/Makefile.am: + * validate/configure.ac: + * validate/pkgconfig/Makefile.am: + * validate/pkgconfig/gst-validate-uninstalled.pc.in: + * validate/pkgconfig/gst-validate.pc.in: + validate: Add .pc files so applications can link against us + +2013-09-15 15:11:53 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + mi-info: extact helper to format times + +2013-09-13 08:16:34 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + mi-info: comment updates + +2013-09-13 08:12:34 +0200 Stefan Sauer + + * mediainfo/TODO: + TODO: update planing + +2013-09-13 08:12:05 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + mi-info: extract ui-helper for format/codec-rows + +2013-09-13 07:55:14 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + mi-info: extract ui helper for adding an entry + A helper to add a label + str formatted details as a table row. + +2013-09-11 08:21:06 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + mi-info: extract more common ui code into helpers + +2013-09-09 18:48:10 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + mi-info: reshuffle container widgets + Prepare for handling nested containers. + +2013-09-09 18:37:24 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + mi-info: code cleanups + First reset the info pane and then check/update. This fixes not resetting the + tabs on info==null. + +2013-09-09 18:36:47 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + mi-info: fix type + It is 'Notebook' and not 'NoteBook'. + +2013-09-06 08:56:05 +0200 Stefan Sauer + + * mediainfo/src/mi-app.vala: + * mediainfo/src/mi-info.vala: + * mediainfo/src/mi.vala: + mi: code cleanups, comments + +2013-09-06 08:03:51 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + mi-info: fix tab-index in compact mode + +2013-09-05 09:18:26 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: sort streams by stream_id + This way we are activating the right stream when switching tabs. + +2013-09-05 09:18:04 +0200 Stefan Sauer + + * mediainfo/TODO: + TODO: spelling fixes and update + +2013-09-04 09:17:28 +0200 Stefan Sauer + + * mediainfo/src/mi-app.vala: + * mediainfo/src/mi.vala: + app: allow giving an uri instead of a directory as a startup arg + This way we can play streams. + +2013-09-04 09:16:47 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + * mediainfo/src/mi-preview.vala: + preview: fix minimal size + We need some minial size, otherwise we can enlarge, but not shrink the window. + +2013-09-04 09:15:34 +0200 Stefan Sauer + + * mediainfo/TODO: + TODO: small ideas update + +2013-09-04 09:15:07 +0200 Stefan Sauer + + * mediainfo/HACKING: + * mediainfo/src/mi-info.vala: + HACKING: update instructions + +2013-09-03 22:03:19 +0200 Stefan Sauer + + * mediainfo/src/Makefile.am: + * mediainfo/src/mi-info.vala: + * mediainfo/src/mi-preview.vala: + preview: extract preview area as separate widget + +2013-09-03 07:41:46 +0200 Stefan Sauer + + * mediainfo/src/mi-app.vala: + * mediainfo/src/mi-info.vala: + * mediainfo/src/mi.vala: + mi: update my name and years + +2013-09-02 22:25:09 +0200 Stefan Sauer + + * mediainfo/HACKING: + * mediainfo/src/Makefile.am: + * mediainfo/vapi/config.vapi: + mi: set the log domain + +2013-09-02 09:52:30 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + mi-info: comment updates + +2013-09-02 09:49:51 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + mi-info: turn stdout.printf into debug log calls + +2013-09-02 09:41:24 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + mi-info: add todo for stream switching + +2013-09-02 09:40:58 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + mi-info: playbin handles force-aspect-ration in 1.0 + +2013-08-30 10:53:13 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: improve sizing + We're now hinting the scrolled window about the content size to avoid empty space + scrolling. + +2013-08-30 08:41:48 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + mi-info: basic subtitle support + +2013-08-30 08:26:37 +0200 Stefan Sauer + + * mediainfo/src/mi-app.vala: + * mediainfo/src/mi-info.vala: + video-area: improve resizing of the video area + The browser pane does not expand by default. Track aspect-ration for the + currently displayed object. Use an aspect frame as a container for the + drawing-area. + +2013-08-29 07:51:13 +0200 Stefan Sauer + + * mediainfo/README: + * mediainfo/TODO: + todo: update planning and ideas + +2013-08-28 21:27:36 +0200 Stefan Sauer + + * mediainfo/src/mi-app.vala: + mi-app: use the newer gtk api with orientation + +2013-08-28 20:26:54 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + mi-info: improve wikilink mapping + Try codecname and then caps name to get wiki links. Also show caps for the container. + +2013-08-27 23:57:06 +0200 Stefan Sauer + + * mediainfo/configure.ac: + * mediainfo/src/mi-info.vala: + mi: port to gst-1.0 and gtk+3 + +2013-08-18 16:01:33 +0200 Stefan Sauer + + * mediainfo/TODO: + TODO: some link for inspiration + +2012-10-23 15:54:06 +0200 Stefan Sauer + + * mediainfo/src/mi-app.vala: + * mediainfo/src/mi-info.vala: + cleanup. update name and year, queue a redraw for album art + +2012-10-23 15:21:34 +0200 Stefan Sauer + + * mediainfo/src/mi-app.vala: + * mediainfo/src/mi-info.vala: + porting: update on vala changes + +2011-06-21 15:05:37 +0200 Stefan Sauer + + * mediainfo/autogen.sh: + * mediainfo/configure.ac: + buid: fix the bootstrapping + Create the macrodir. Remove the GETTXT macro that was clashing with INTLTOOL. + +2011-03-04 18:14:06 +0200 Stefan Sauer + + * mediainfo/README: + README: more planning + +2011-03-04 18:13:34 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: add wikilinks for two more codecs + +2011-02-18 17:36:30 +0200 Stefan Sauer + + * mediainfo/README: + README: planning + +2011-01-29 14:41:28 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: reset container and duration fields if file is not discoverable + Before the previous text was left. + +2011-01-25 15:07:07 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: change the order of asyn disco calls + Starting disco before enqueueuing uris seems to make it work. + +2011-01-25 14:52:05 +0200 Stefan Sauer + + * mediainfo/README: + planing: CBR/VBR info + +2011-01-24 23:42:15 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + mi-info: prepare for async discovery + The async api usage is not yet activated due to some uncertanty in the api use. + +2011-01-24 23:40:23 +0200 Stefan Sauer + + * mediainfo/README: + * mediainfo/src/mi-info.vala: + comments: planning and code comments + +2011-01-24 22:28:32 +0200 Stefan Sauer + + * mediainfo/src/Makefile.am: + Makefile: indenting + +2011-01-24 11:24:26 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: add more wikilinks + +2011-01-17 23:09:54 +0200 Stefan Sauer + + * mediainfo/configure.ac: + release: bump versions and back to development + +2011-01-17 23:05:54 +0200 Stefan Sauer + + * mediainfo/NEWS: + release: prepare for release + +2011-01-17 23:06:07 +0200 Stefan Sauer + + * mediainfo/HACKING: + docs: more maintainer info + +2011-01-16 14:25:19 +0200 Stefan Sauer + + * mediainfo/HACKING: + docs: add simple HACKING file + +2011-01-14 23:15:42 +0200 Stefan Sauer + + * mediainfo/src/Makefile.am: + build: fix distcheck + +2011-01-14 23:01:08 +0200 Stefan Sauer + + * mediainfo/src/mi-app.vala: + app: add idea for stream-open dialog + +2011-01-14 22:41:20 +0200 Stefan Sauer + + * mediainfo/configure.ac: + * mediainfo/po/POTFILES.in: + * mediainfo/src/Makefile.am: + * mediainfo/src/gst-mi.desktop.in: + desktop: add a desktop file + +2011-01-14 22:40:03 +0200 Stefan Sauer + + * mediainfo/src/Makefile.am: + * mediainfo/src/gst-mi.png: + * mediainfo/src/gst-mi.svg: + * mediainfo/src/mi-app.vala: + icon: add an application icon + Set as default icons, so that it it used for windows and in about dialog. + +2011-01-14 21:53:34 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: enable double buffering when displaying album art + +2011-01-14 18:39:01 +0200 Stefan Sauer + + * mediainfo/README: + * mediainfo/src/mi-info.vala: + info: handle album-art + Decode and draw the album art into the video window. + +2011-01-14 11:19:16 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: filter buffers from tags and add some planning comments + +2011-01-14 11:18:45 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: add comment with wikipedia alternative for links + +2011-01-13 12:21:46 +0200 Stefan Sauer + + * mediainfo/src/mi-app.vala: + app: add about dialog + +2011-01-13 12:05:35 +0200 Stefan Sauer + + * mediainfo/po/POTFILES.in: + * mediainfo/po/POTFILES.skip: + i18n: manage translatable files + +2011-01-13 12:03:00 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + * mediainfo/vapi/vapi.gstreamer-pbutils-0.10.patch: + vapi: no more need for patching the vapi file + +2011-01-13 11:56:37 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: switch stream callback for compact layout + +2011-01-13 11:56:18 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: verified link + +2011-01-13 10:03:32 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: add compact_layout mode + For screen-heights <= 600 pixels use a single notebook for all streams. + +2011-01-13 09:15:04 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: add more wikilinks and use it for streams too + +2011-01-12 10:17:56 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: add a hashmap with wiki links and start using them + The container description will be turned into a link if we have a known + wikipedia article for it. + +2011-01-04 14:56:40 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + mi-info: more ui layout planning + +2010-12-21 23:09:43 +0200 Stefan Sauer + + * mediainfo/README: + README: planning + +2010-12-21 14:54:56 +0200 Stefan Sauer + + * mediainfo/src/mi-app.vala: + menu: use alternative way to get the key number + +2010-12-21 13:55:02 +0200 Stefan Sauer + + * mediainfo/src/mi-app.vala: + menu: add F11 accelerator for fullscreen + +2010-12-21 13:21:34 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: blacklist "norminal-bitrate" too + We show that above already. + +2010-12-21 13:18:38 +0200 Stefan Sauer + + * mediainfo/src/mi-app.vala: + menu: add View menu with fullscreen item + +2010-12-21 12:01:40 +0200 Stefan Sauer + + * mediainfo/src/mi-app.vala: + * mediainfo/src/mi-info.vala: + layout: improve the layout on small screens + Pack the info view info a scrolled window. Use an extra paned to allow resizing + the video pane. Minimize padding on paned widgets. + +2010-12-21 11:27:57 +0200 Stefan Sauer + + * mediainfo/Makefile.am: + * mediainfo/configure.ac: + build: updates for gettext + +2010-12-16 23:06:57 +0200 Stefan Sauer + + * mediainfo/README: + README: planning + +2010-12-16 23:02:31 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: show the fps as a double + 23.97 fps is easier to read that 10000000 / 417083. + +2010-12-16 11:42:34 +0200 Stefan Sauer + + * mediainfo/autogen.sh: + autogen.sh: fix silly typo + +2010-12-15 11:46:08 +0200 Stefan Sauer + + * mediainfo/README: + README: planning + +2010-11-30 12:20:44 +0200 Stefan Sauer + + * mediainfo/README: + README: update todo lists + +2010-11-30 11:19:35 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: make urls in tags clickable + +2010-11-30 11:18:37 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: make labels selectable for copy'n'paste + +2010-11-10 08:50:48 +0200 Stefan Sauer + + * mediainfo/README: + * mediainfo/src/mi-info.vala: + planning: comments and todos + +2010-11-08 11:52:11 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: improve tag filtering + Hide duration as well. Also skip all tags where the name starts with "private-". + +2010-11-08 11:40:06 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: redo tag list formatting + Loop over tags and serialize items. Skip some already shown info. + +2010-11-08 10:38:39 +0200 Stefan Sauer + + * mediainfo/README: + * mediainfo/src/mi-app.vala: + * mediainfo/src/mi-info.vala: + maintenance: code cleanups and addition TODO: comments + +2010-11-05 17:17:16 +0200 Stefan Sauer + + * mediainfo/configure.ac: + * mediainfo/src/mi-info.vala: + info: add named video resolutions + Use a gee hashmap for named video resolutions (e.g. VGA) and show those in the + info. + +2010-11-04 12:47:14 +0200 Stefan Sauer + + * mediainfo/README: + * mediainfo/src/mi-info.vala: + * mediainfo/vapi/vapi.gstreamer-pbutils-0.10.patch: + info: show human readable container format name + Update the patch for vala bindings. Update README as getting the contaienr caps + is already possible. + +2010-11-04 10:49:40 +0200 Stefan Sauer + + * mediainfo/vapi/vapi.gstreamer-pbutils-0.10.patch: + vapi: add current patch for gstreamer-pbutils vapi metadata + +2010-11-04 10:46:43 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: show stream tags as multiline label + +2010-11-04 10:15:19 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: show misc stream info, if available + +2010-11-04 09:48:55 +0200 Stefan Sauer + + * mediainfo/src/mi-app.vala: + * mediainfo/src/mi.vala: + app: add directory property and use it as default location for browsing + Allow passing a directory as a commandline arg. If given use that as the default + location, otherwise use current working dir. + +2010-11-04 09:46:58 +0200 Stefan Sauer + + * mediainfo/src/mi.vala: + mi: we need to open the default display + The ui was crashing otherwise. Opening the default display seems to be the + default behaviour for gtk_init(). + +2010-11-03 13:59:44 +0200 Stefan Sauer + + * mediainfo/src/mi.vala: + mi: add basic goption usage + Only --version works right now. + +2010-11-03 10:47:04 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: handle video area expose also if we have no video + +2010-11-03 10:34:57 +0200 Stefan Sauer + + * mediainfo/README: + README: add todo for gst-discoverer + +2010-11-03 10:33:43 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: use File.query to get file info + Use the file info to query content-type and icon. Show file-type icon in the UI. + +2010-11-03 10:01:04 +0200 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: prepare to show human readable container format info + gst-discoverer does not yet provide it unfortunately. + +2010-10-28 17:34:38 +0300 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: add more TODO + +2010-10-28 17:34:15 +0300 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: switch streams when switching tabs + +2010-10-28 17:33:54 +0300 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: whitespace fix + +2010-10-28 16:45:55 +0300 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: add human readable code info + +2010-10-28 16:39:35 +0300 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: improve resolution + Orint it as "w x h" and add idea to convert to human readable string + +2010-10-28 16:34:08 +0300 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: add remaining audio/video info fields + +2010-10-28 03:08:28 +0300 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: turn stream widgets into tables and add bitrate + +2010-10-28 02:15:56 +0300 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: initialy paint video area black + +2010-10-28 01:41:48 +0300 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: ellipsize labels to avoid horzontal window growth + +2010-10-28 01:41:07 +0300 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: stop playback on unrealize + Prevent "BadDrawable" errors. + +2010-10-27 02:18:17 +0300 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: ensure we have a native widnow for the drawing_area + Fixes BadID x errors. + +2010-10-27 02:02:15 +0300 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: add vapi info for ubuntu + +2010-10-25 10:54:07 +0300 Stefan Sauer + + * mediainfo/README: + REDME: planning + +2010-10-20 00:44:18 +0300 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: get a/mediainfo/v streams and show caps + +2010-10-19 23:04:42 +0300 Stefan Sauer + + * mediainfo/src/mi-app.vala: + app: use a hpane instead of the file-chooser preview widget + This allows the user to modify the size. + +2010-10-19 22:53:28 +0300 Stefan Sauer + + * mediainfo/src/mi-app.vala: + * mediainfo/src/mi-info.vala: + app: only run discover for files + +2010-10-18 22:50:02 +0300 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: start to use discoverer and improve the ui + Organize the info pane as a table. Add mime type and duration fields to + container section. + +2010-10-18 17:38:32 +0300 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: add more ui boilerplate and fixme comments + +2010-10-18 17:14:46 +0300 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: enable discovered + Add comment telling how to regenerate the vapi files. + +2010-10-18 10:31:43 +0300 Stefan Sauer + + * mediainfo/src/mi-info.vala: + info: make the overlay iface work and activate playback + +2010-10-18 09:53:45 +0300 Stefan Sauer + + * mediainfo/src/mi-app.vala: + app: configure the filechooser more. + Go to home dir (maybe annoying feature). Don't show hidden files. + +2010-10-18 09:51:48 +0300 Stefan Sauer + + * mediainfo/src/Makefile.am: + build: fix libraryname + +2010-10-18 00:07:02 +0300 Stefan Sauer + + * mediainfo/src/Makefile.am: + * mediainfo/src/mi-info.vala: + info: add missing libs and enable the overlay code + +2010-10-17 23:54:06 +0300 Stefan Sauer + + * mediainfo/gst-mediainfo.anjuta: + anjuta: add anjuta project + +2010-10-17 23:53:23 +0300 Stefan Sauer + + * mediainfo/src/mi-app.vala: + info: add playbin2 and overlay iface handling + +2010-10-17 23:52:37 +0300 Stefan Sauer + + * mediainfo/src/mi-app.vala: + * mediainfo/src/mi-info.vala: + * mediainfo/src/mi.vala: + mi: init gst + +2010-10-17 23:21:16 +0300 Stefan Sauer + + * mediainfo/configure.ac: + * mediainfo/src/mi-info.vala: + info: start adding gstreamer discoverer + +2010-10-17 22:37:26 +0300 Stefan Sauer + + * mediainfo/src/mi.vala: + mi: code style + +2010-10-17 22:36:39 +0300 Stefan Sauer + + * mediainfo/src/Makefile.am: + * mediainfo/src/mi-app.vala: + * mediainfo/src/mi-info.vala: + info,app: move label to separate info class + Info class will run discover and have the detailed UI. + +2010-10-17 22:35:50 +0300 Stefan Sauer + + * mediainfo/README: + README: add sample discover output + +2010-10-17 22:16:27 +0300 Stefan Sauer + + * mediainfo/src/mi-app.vala: + * mediainfo/src/mi.vala: + app: add basic widget and do basic preview + Add a menu bar and a file browser. Set up a preview callback. + +2010-10-17 00:18:20 +0300 Stefan Sauer + + * mediainfo/Makefile.am: + * mediainfo/po/LINGUAS: + po: add LINGUAS file and handle generated files under po/ + +2010-10-16 23:56:55 +0300 Stefan Sauer + + * mediainfo/autogen.sh: + build: tweak autogen.sh + Generate autoregen.sh, run configure. + +2010-10-16 23:48:38 +0300 Stefan Sauer + + * mediainfo/AUTHORS: + * mediainfo/COPYING: + * mediainfo/ChangeLog: + * mediainfo/Makefile.am: + * mediainfo/NEWS: + * mediainfo/autogen.sh: + * mediainfo/configure.ac: + * mediainfo/git.mk: + * mediainfo/po/POTFILES.in: + * mediainfo/po/POTFILES.skip: + * mediainfo/src/Makefile.am: + * mediainfo/src/mi-app.vala: + * mediainfo/src/mi.vala: + * mediainfo/vapi/Makefile.am: + * mediainfo/vapi/config.vapi: + *: initial boilerplate + +2010-10-16 22:43:20 +0300 Stefan Sauer + + * mediainfo/README: + docs: start collecting ideas + +2013-09-13 11:43:33 -0300 Thibault Saunier + + * validate/data/seek_forward_backward.scenario: + * validate/data/simple_backward.scenario: + * validate/gst/validate/gst-validate-scenario.c: + data: Port remaning scenario files to new format + And add support to user declared timestamps -1.0 as GST_CLOCK_TIME_NONE + +2013-09-09 19:04:48 -0300 Thibault Saunier + + * validate/data/alternate_fast_backward_forward.scenario: + * validate/data/fast_backward.scenario: + * validate/data/fast_forward.scenario: + * validate/data/pause_resume.scenario: + * validate/data/seek_backward.scenario: + * validate/data/seek_forward.scenario: + * validate/data/simple_seeks.scenario: + * validate/gst/validate/gst-validate-internal.h: + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + * validate/gst/validate/validate.c: + scenario: Rework scenarios to be: 1- Simpler to write them, 2- extendible + Make the scenario files a list of GstStructure-s as strings + +2013-09-09 19:05:24 -0300 Thibault Saunier + + * validate/gst/validate/gst-validate-bin-monitor.c: + bin-monitor: Add a way to specify pipelines on which to set scenarios + When used with LD_PRELOAD, the application might use various pipelines + for several different thing, we need to make it possible to spcify a + specific pipeline (or set of pipelines) on which to run the scenario. + The format is in the form of: + scenario_name:pipelinename_pattern* + +2013-09-09 19:01:44 -0300 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + scenario: Try to run scenarios in development first + +2013-09-09 17:40:36 +0200 Edward Hervey + + * .gitmodules: + * common: + * validate/.gitmodules: + * validate/autogen.sh: + * validate/common: + Adapt submodule usage for gst-devtools + +2013-09-02 15:42:40 +0200 Edward Hervey + + * validate/gst/validate/.gitignore: + * validate/tools/.gitignore: + tools: Update .gitignore for tools move + +2013-09-05 16:15:40 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-pad-monitor.c: + pad-monitor: avoid false positives when a seek fails + Remove the expected seqnums for events when a seek fails, preventing + false positives at the final report + +2013-09-05 04:34:42 -0400 Vincent Penquerc'h + + * validate/gst/validate/gst-validate-monitor-preload.c: + * validate/gst/validate/gst-validate-runner.c: + * validate/gst/validate/gst-validate-runner.h: + * validate/tools/gst-validate.c: + monitor-preload: schedule a report printout at exit + Conflicts: + tools/gst-validate.c + +2013-09-04 11:09:50 -0400 Vincent Penquerc'h + + * validate/tools/gst-validate.c: + gst-validate: ensure the top level element is a pipeline + For instance, "fakesrc" will return a fakesrc, not a pipeline. + This is similar to what gst-launch does, and avoids calling + pipeline API on a non pipeline object (and thus asserting). + +2013-09-04 11:05:48 -0400 Vincent Penquerc'h + + * validate/tools/gst-validate.c: + gst-validate: do not try to use a pipeline which failed to create + Instead, error out properly with the actual error, if available. + +2013-09-04 10:50:11 -0400 Vincent Penquerc'h + + * validate/tools/gst-validate.c: + gst-validate: initialize gst/glib before use in scenario listing + Also ensure that if just -l is passed, we don't try creating a + non existent pipeline. + This makes gst-validate -l work properly again. + +2013-09-05 11:47:21 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-pad-monitor.c: + pad-monitor: fix typo on macro usage + Pass the correct variable to macro + +2013-09-05 11:46:46 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-pad-monitor.c: + pad-monitor: allow flushing flow returns when pad is flushing + It should always be acceptable to return GST_FLOW_FLUSHING when the + pad is flushing + +2013-09-03 15:58:20 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-pad-monitor.c: + pad-monitor: removing bad check + Elements are allowed to accumulate segments, they don't have to push + 1:1 segments as they receive + +2013-09-03 15:35:36 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-pad-monitor.c: + * validate/gst/validate/gst-validate-pad-monitor.h: + pad-monitor: buffer timestamp ranges check + Improve buffer timestamp range check: + * Only do it for encoders or decoders + * Audio has an acceptable tolerance of 100ms + To do this, keep track of the caps on the pad and store + if it is dealing with audio or video + +2013-09-03 15:17:05 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-pad-monitor.c: + pad-monitor: move caps check to common event handling + Allows both src and sink pad to keep track of the current caps, but + the duplicated caps check is still only applied to sink pads as + src pads can push the same caps multiple times when it isn't linked + +2013-09-02 20:41:35 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-pad-monitor.c: + pad-monitor: fix reference handling for expired events list + +2013-09-02 16:08:19 -0300 Thiago Santos + + * validate/README: + * validate/docs/validate-usage.txt: + docs: update and improve + Thanks to Thibault Saunier for most of the explanatory texts + +2013-09-02 13:22:51 -0300 Thiago Santos + + * validate/README: + * validate/data/Makefile.am: + * validate/docs/qa-design.txt: + * validate/docs/qa-usage.txt: + * validate/docs/validate-design.txt: + * validate/docs/validate-usage.txt: + * validate/gst/validate/gst-validate-bin-monitor.c: + * validate/gst/validate/gst-validate-element-monitor.c: + * validate/gst/validate/gst-validate-monitor.c: + * validate/gst/validate/gst-validate-pad-monitor.c: + * validate/gst/validate/gst-validate-reporter.c: + * validate/gst/validate/gst-validate-scenario.c: + Replacing mentions of qa with validate + +2013-09-02 12:18:07 -0300 Edward Hervey + + * validate/gst/validate/gst-validate-pad-monitor.c: + pad-monitor: Check if iterator exists before trying to use it + +2013-09-02 12:15:24 -0300 Edward Hervey + + * validate/gst/validate/gst-validate-pad-monitor.c: + pad-monitor: make debug log more readable + Use pad as the debug object to make logs more meaningful. + Also adds a FIXME note + +2013-09-02 12:11:25 -0300 Edward Hervey + + * validate/gst/validate/gst-validate-element-monitor.c: + element-monitor: protect agains elements that have no klass + +2013-09-02 11:37:02 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-pad-monitor.c: + * validate/gst/validate/gst-validate-pad-monitor.h: + pad-monitor: use activate-mode function to detect when to clear pad data + Clear as much as a flush-stop when pad is deactivated + +2013-08-23 09:15:29 +0200 Edward Hervey + + * validate/gst/validate/gst-validate-pad-monitor.c: + * validate/gst/validate/gst-validate-pad-monitor.h: + WIP: pad-monitor: Fix serialized event order check + +2013-09-02 10:46:55 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-pad-monitor.c: + * validate/gst/validate/gst-validate-pad-monitor.h: + pad-monitor: also track eos event that should be emitted after a seek + When seeking out of the media file length, the element should push an + EOS with the same seqnum of the seek event + +2013-09-02 10:46:42 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-scenario.c: + scenario: add missing space + +2013-08-25 19:53:27 +0200 Edward Hervey + + * validate/gst/validate/gst-validate-report.c: + * validate/tools/gst-validate.c: + validate: prettify output of results + Makes the result a bit more readable than a compact multi-line list. + FIXME: Figure out how to print the description of the issues (which can + spawn multiple lines) in a nice way. + +2013-08-29 14:27:34 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-scenario.c: + scenario: add missing line break after print + +2013-08-29 14:26:05 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-pad-monitor.c: + pad-monitor: avoid tracking tag events + Tag events are hard to track and check if properly serialized because + they mutate too much inside elements. There is no reliable way currently + to match a tag event pushed into an element and another tag event + leaving the element (other than if the pointers are actually the same). + +2013-08-29 11:48:33 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-pad-monitor.c: + pad-monitor: only do combined return checks for demuxers + Seems like the only place that gstreamer elements should really + care about it + +2013-08-29 11:47:58 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-pad-monitor.c: + pad-monitor: add two useful macros for readability + Avoids using long macros and having to check for pad-monitor parent + existance + +2013-08-28 06:07:40 -0400 Vincent Penquerc'h + + * validate/gst/validate/gst-validate-media-info.c: + * validate/gst/validate/gst-validate-media-info.h: + media-info: add a track switching test + This test will find the first input selector with more than one + sink pad, and cycle through them till it gets back to the original + one. Five seconds between switches. The test checks that some data + was sent from the input selector when each of the sink pads was + selected. + +2013-08-23 09:58:58 -0400 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + scenario: Print on stdout when we seek + +2013-08-23 09:39:05 -0400 Thibault Saunier + + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-report.h: + * validate/gst/validate/gst-validate-scenario.c: + validate: Report an issue result of query state that position > duration + +2013-08-22 16:52:45 -0400 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + scenario: Execute action whenever we pass the expected position + We know are sequential so whenever the wanted position is passed we + should execute the action. + This avoid issue with the tolerance when we have high rate playback + +2013-08-22 12:16:55 -0400 Thibault Saunier + + * validate/tools/gst-validate-transcoding.c: + validate: Dump pipeline for each state change + Ala gst-launch + +2013-08-22 11:17:26 -0400 Thibault Saunier + + * validate/tools/gst-validate-transcoding.c: + transcoding: Print duration regularly + +2013-08-22 10:51:49 -0400 Thibault Saunier + + * validate/tools/gst-validate.c: + validate: Print state changes to help debugging + +2013-08-28 16:58:11 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-bin-monitor.c: + * validate/gst/validate/gst-validate-default-overrides.c: + * validate/gst/validate/gst-validate-element-monitor.c: + * validate/gst/validate/gst-validate-media-info.c: + * validate/gst/validate/gst-validate-monitor-factory.c: + * validate/gst/validate/gst-validate-monitor-preload.c: + * validate/gst/validate/gst-validate-monitor.c: + * validate/gst/validate/gst-validate-override-registry.c: + * validate/gst/validate/gst-validate-override.c: + * validate/gst/validate/gst-validate-pad-monitor.c: + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-reporter.c: + * validate/gst/validate/gst-validate-runner.c: + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/validate.c: + * validate/tools/gst-validate-media-check.c: + * validate/tools/gst-validate-transcoding.c: + * validate/tools/gst-validate.c: + licenses: improving licensing info on all files + +2013-08-28 16:49:07 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-pad-monitor.c: + pad-monitor: fix typo when acessing parents data + +2013-08-27 18:23:09 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-pad-monitor.c: + pad-monitor: reset buffer timestamp data after a flush + As the pad/element also clears its internal state + +2013-08-27 16:16:08 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-pad-monitor.c: + pad-monitor: also track flush events on probes + +2013-08-27 11:56:33 -0300 Thiago Santos + + * validate/Makefile.am: + * validate/autogen.sh: + * validate/configure.ac: + * validate/gst/validate/Makefile.am: + * validate/gst/validate/gst-validate-media-check.c: + * validate/gst/validate/gst-validate-transcoding.c: + * validate/gst/validate/gst-validate.c: + * validate/tools/Makefile.am: + * validate/tools/gst-validate-media-check.c: + * validate/tools/gst-validate-transcoding.c: + * validate/tools/gst-validate.c: + tools: moving applications from gst/validate to tools + Keeps the CLI applications separate from the libs files + +2013-08-27 05:15:19 -0400 Vincent Penquerc'h + + * validate/gst/validate/gst-validate-scenario.c: + gst-validate-scenario: fix scenario listing missing installed ones + Only scenarii in the current directory or the user's home directory + were being listed. + +2013-08-27 05:08:46 -0400 Vincent Penquerc'h + + * validate/gst/validate/gst-validate-transcoding.c: + gst-validate-transcoding: fix help text to refer to URIs as URIs + Referring to them as files is confusing, as you'll try to use files + and not URIs. + +2013-08-27 04:38:52 -0400 Vincent Penquerc'h + + * validate/docs/qa-design.txt: + * validate/docs/qa-usage.txt: + docs: minor spelling/grammar fixes + +2013-08-27 11:48:00 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-pad-monitor.c: + pad-monitor: Move repeated caps to test only on sinkpads + Testing on source pads can lead to false positives when pads are + unlinked. The caps event is sticky and will be pushed again later + when another buffer/event is pushed, leading to an acceptable + situation to push the caps twice. + +2013-08-26 20:30:07 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-element-monitor.c: + * validate/gst/validate/gst-validate-element-monitor.h: + * validate/gst/validate/gst-validate-pad-monitor.c: + pad-monitor: add another acceptable flow return combination scenarios + A demuxer knows when to return EOS after samples are over, so it is + ok for it to return even when all src pads returned OK + +2013-08-26 18:38:27 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-pad-monitor.c: + pad-monitor: improve serialized event checks + If the event was already found at the first position of the array, it + shouldn't be searched on the rest of it. + This removes lots of false positives. + +2013-08-26 18:36:06 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-pad-monitor.c: + pad-monitor: fix aggregate flow return check for error situations + Flow flushing must be returned upstream to indicate an error situation + downstream + +2013-08-26 20:31:22 -0300 Thiago Santos + + * validate/gst/validate/gst-validate.c: + gst-validate: print error message when starting the pipeline fails + Instead of just exiting silently + +2013-08-23 09:16:43 +0200 Edward Hervey + + * validate/gst/validate/gst-validate-pad-monitor.c: + * validate/gst/validate/gst-validate-pad-monitor.h: + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-report.h: + pad-monitor: New check for duplicate caps event + We shouldn't get/push twice caps that are identical + +2013-08-23 17:26:51 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-media-info.c: + media-info: avoid glib assert + +2013-08-23 11:38:15 +0200 Edward Hervey + + * validate/gst/validate/gst-validate-pad-monitor.c: + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-reporter.h: + * validate/gst/validate/gst-validate-scenario.c: + report: Avoid repeating long macros + Makes the code a bit more readable and compact + +2013-08-23 11:07:40 +0200 Edward Hervey + + * validate/gst/validate/gst-validate-report.c: + validate-report: Fix critical flag handling + criticals are warnings/issues also + warnings are issues also + +2013-08-20 17:25:48 -0400 Thibault Saunier + + * validate/data/Makefile.am: + * validate/data/alternate_fast_backward_forward.scenario: + data: Add a test that alternates (fast) backward and forward playback + +2013-08-19 10:03:04 -0400 Thibault Saunier + + * validate/data/Makefile.am: + * validate/data/seek_backward.scenario: + * validate/data/seek_forward.scenario: + data: Add a seek_backward/forward scenarios + +2013-08-19 10:02:35 -0400 Thibault Saunier + + * validate/data/simple_seeks.scenario: + * validate/gst/validate/gst-validate-scenario.c: + scenario: Have GstClockTime as second (in double) inside scenario files + Making it easier to read + +2013-08-15 17:32:23 +0200 Thibault Saunier + + * validate/data/Makefile.am: + * validate/data/fast_backward.scenario: + * validate/data/fast_forward.scenario: + * validate/data/simple_backward.scenario: + data: Add fast_forward/backward and simple_backward scenarios + +2013-08-15 12:34:09 +0200 Thibault Saunier + + * validate/data/Makefile.am: + * validate/data/seek_forward_backward.scenario: + * validate/gst/validate/gst-validate-scenario.c: + data: Add a Backward and Forward seeking scenario + +2013-08-15 12:17:43 +0200 Thibault Saunier + + * validate/data/Makefile.am: + * validate/data/pause_resume.scenario: + data: Add a Pause/Resume scenario + +2013-08-19 14:13:10 -0400 Thibault Saunier + + * validate/gst/validate/gst-validate-transcoding.c: + * validate/gst/validate/gst-validate.c: + validate: Set return value of apps to -1 only if a critical issues was reported + Conflicts: + gst/validate/gst-validate-transcoding.c + gst/validate/gst-validate.c + +2013-08-16 16:41:50 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-transcoding.c: + transcoding: Make sure to initialize Gst before parsing options + Avoiding to break the help + +2013-08-15 15:59:22 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-transcoding.c: + transcoding: Connect to the bus signals watch as the main watch might already be connected + +2013-08-15 17:31:17 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + scenario: Start monitoring the position only when the pipeline starts playing + Otherwize seeking with a playback_time=0 won't work properly + +2013-08-15 17:30:34 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + scenario: Fix negative rate management + Properly parse the it has a gdouble and set the stop position of the seek as + seeked_position if the rate is negative + + Add some debug + +2013-08-15 12:33:23 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + scenario: Actions order in xml file is the order in which they must be executed + When seeking we might want to execute seeks at a playback time inferior than previous + seek, so we need to be able to define the order in which actions have to be + executed, the simplest way is to just concider that actions are always + order in the XML files. + + Add some more debugs + Conflicts: + gst/validate/gst-validate-scenario.c + +2013-08-15 15:57:52 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + scenario: Rename the seeks list to actions, and initialize action to 0 when allocating + +2013-08-16 12:17:34 +0200 Thibault Saunier + + * validate/data/Makefile.am: + * validate/data/simple_seeks.scenario: + * validate/data/simple_seeks.xml: + * validate/gst/validate/gst-validate-scenario.c: + scenario: Rename scenario xml files extension to .scenario + +2013-08-15 12:18:56 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate.c: + validate: Connect to the bus signals watch as the main watch might already be connected + +2013-08-16 12:50:51 +0200 Thibault Saunier + + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + * validate/gst/validate/gst-validate-transcoding.c: + * validate/gst/validate/gst-validate.c: + validate: Add a way to list avalaible scenarios + Conflicts: + gst/validate/gst-validate-transcoding.c + +2013-08-22 10:35:50 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-transcoding.c: + gst-validate-transcoding: add signal handling and issues printing + Update to have the same features as gst-validate. + 1) Handle interrupts properly, with the additional of having the + 'eos-on-shutdown' argument that sends EOS to the pipeline. This is + very useful for transcoding processes to finish correctly. + 2) Print issues on the end of application + +2013-08-22 10:08:13 -0300 Thiago Santos + + * validate/gst/validate/gst-validate.c: + gst-validate: add interrupt handler + Handle interrupt properly to still print issues when exiting + +2013-08-21 18:21:41 +0200 Edward Hervey + + * validate/gst/validate/gst-validate-pad-monitor.c: + pad-monitor: Fix source pad probe handling + type is a bitmask and not an enum + +2013-08-21 13:10:42 -0300 Thiago Santos + + * validate/gst/validate/gst-validate.c: + gst-validate: fix documentation after debug category changes + +2013-08-21 18:00:16 +0200 Edward Hervey + + * validate/gst/validate/gst-validate-reporter.c: + validate-reporter: More comprehensive debug message + Some issues don't have any arguments, so put the full details in. + +2013-08-20 11:43:07 +0200 Edward Hervey + + * validate/gst/validate/Makefile.am: + * validate/gst/validate/gst-validate-bin-monitor.c: + * validate/gst/validate/gst-validate-element-monitor.c: + * validate/gst/validate/gst-validate-internal.h: + * validate/gst/validate/gst-validate-monitor.c: + * validate/gst/validate/gst-validate-override-registry.c: + * validate/gst/validate/gst-validate-override.c: + * validate/gst/validate/gst-validate-pad-monitor.c: + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-reporter.c: + * validate/gst/validate/gst-validate-runner.c: + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/validate.c: + validate: Only use one debugging category: validate + There's no point in having a different debug category per file, you + can filter it by source filename if you *really* want that. + +2013-08-21 12:11:40 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-media-check.c: + * validate/gst/validate/gst-validate.c: + gst-validate: print issues at the end + And improve documentation about usage + +2013-08-21 11:03:19 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-media-check.c: + * validate/gst/validate/gst-validate-media-info.c: + * validate/gst/validate/gst-validate-media-info.h: + media-check: add results file comparison + Adds a new expected-results argument to receive a file that is used + as a base for comparison with the new results. In case differences are + found, the application will print those issues. + +2013-08-20 17:10:44 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-reporter.c: + reporter: do not print issues to stdout + +2013-08-20 15:44:10 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-media-info.c: + media-info: fix playback tests + They weren't waiting for the pipeline to properly change state + before sending seek events, that would cause some events to + return TRUE even if they were not handled + +2013-08-20 15:42:54 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-media-check.c: + media-check: return nonzero if a test failed + +2013-08-20 13:24:31 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-media-info.c: + * validate/gst/validate/gst-validate-media-info.h: + media-info: add playback and reverse-playback tests + The tests are very simple as they only write the first error they + found during playback. If no error is set, an empty string is + printed. + The playback pipeline isn't monitored with validate monitors for now + +2013-08-20 11:43:06 -0300 Thiago Santos + + * validate/gst/validate/Makefile.am: + * validate/gst/validate/gst-validate-file-check.c: + * validate/gst/validate/gst-validate-media-check.c: + rename: gst-validate-file-check -> gst-validate-media-check + It not only validates files, takes any URI + +2013-08-20 11:41:15 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-media-info.c: + * validate/gst/validate/gst-validate-media-info.h: + media-info: add stream topology parsing + Currently it only saves/loads the main type, but all topology is + already being parsed for future use + +2013-08-19 16:52:12 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-media-info.c: + media-info: add duration and seekable entries + Add duration entry in ns and seekable as a boolean to a new group + 'media-info' + +2013-08-19 16:38:13 -0300 Thiago Santos + + * validate/gst/validate/Makefile.am: + * validate/gst/validate/gst-validate-file-check.c: + * validate/gst/validate/gst-validate-file-checker.c: + * validate/gst/validate/gst-validate-file-checker.h: + * validate/gst/validate/gst-validate-media-info.c: + * validate/gst/validate/gst-validate-media-info.h: + * validate/gst/validate/gst-validate-transcoding.c: + * validate/gst/validate/validate.h: + media-info: replacing file-checker with a simpler media-info struct + This struct stores information about a media and tests run on it. It + also has a few helper functions that allows storing the results to a + file and loading it back. + Instead of having the file-checker object that would compare the + extracted values from the file to expected results set to its properties, + the media-info will store the values and it will be possible to compare + old media-info with new media-info from the same file. This allows + tracking improvements and regressions on different gstreamer versions. + Right now, the media-info is very tiny and doesn't store much info, only + the uri and the file size in bytes, but it will receive more additions in + the upcoming commits for storing duration, media topology, seekability and + playback information. + +2013-08-16 15:15:51 +0200 Edward Hervey + + * validate/.gitignore: + * validate/gst/validate/.gitignore: + .gitignore: Update for 1.0 and cleanup + +2013-08-16 15:05:54 +0200 Edward Hervey + + * validate/configure.ac: + * validate/gst/validate/gst-validate-bin-monitor.h: + * validate/gst/validate/gst-validate-default-overrides.c: + * validate/gst/validate/gst-validate-element-monitor.h: + * validate/gst/validate/gst-validate-monitor-factory.h: + * validate/gst/validate/gst-validate-monitor.c: + * validate/gst/validate/gst-validate-monitor.h: + * validate/gst/validate/gst-validate-override-registry.h: + * validate/gst/validate/gst-validate-override.h: + * validate/gst/validate/gst-validate-pad-monitor.h: + * validate/gst/validate/gst-validate-report.h: + * validate/gst/validate/gst-validate-reporter.c: + * validate/gst/validate/gst-validate-reporter.h: + * validate/gst/validate/gst-validate-runner.c: + * validate/gst/validate/gst-validate-runner.h: + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + * validate/gst/validate/validate.h: + all: Enable more C warnings at build time + And fix the issues: + * Proper forward declaration + * static functions marked properly + * absolute includes + * declaration order + +2013-08-16 14:27:29 +0200 Edward Hervey + + * validate/gst/validate/gst-validate-reporter.c: + reporter: Fix proper debug message output partially + In order for the special gstreamer print argument handler to be used + you can't use g_strdup_printf. You need to pass it the actual va_list. + +2013-08-16 14:26:35 +0200 Edward Hervey + + * validate/gst/validate/gst-validate-pad-monitor.c: + pad-monitor: Handle case where internal pad iterator is NULL + Can happen with inputselector + +2013-08-16 14:25:49 +0200 Edward Hervey + + * validate/gst/validate/gst-validate-pad-monitor.c: + pad-monitor: Don't use signal that doesn't exist + Note that we should just ensure we always get the pads from the parent + +2013-08-16 14:24:12 +0200 Edward Hervey + + * validate/gst/validate/gst-validate-pad-monitor.c: + pad-monitor: Update raw audio caps checks + +2013-08-16 14:23:05 +0200 Edward Hervey + + * validate/gst/validate/gst-validate-monitor.h: + * validate/gst/validate/gst-validate-pad-monitor.c: + pad-monitor: Fix locking issues + We were taking locks twice. + Also add debugging info when taking/releasing locks to help further similar issues + +2013-08-16 11:24:11 +0200 Edward Hervey + + * validate/gst/validate/gst-validate-file-checker.c: + file-checker: GstEncodingProfile is a GObject in 1.0 + +2013-08-15 01:46:27 -0300 Thiago Santos + + * validate/configure.ac: + * validate/gst/validate/gst-validate-bin-monitor.c: + * validate/gst/validate/gst-validate-element-monitor.c: + * validate/gst/validate/gst-validate-file-checker.c: + * validate/gst/validate/gst-validate-override-registry.c: + * validate/gst/validate/gst-validate-pad-monitor.c: + * validate/gst/validate/gst-validate-pad-monitor.h: + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-transcoding.c: + gst-validate: port to 1.0 + +2013-08-15 01:44:59 -0300 Thiago Santos + + * validate/po/POTFILES.in: + po: missing po rename + +2013-08-14 20:03:43 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-bin-monitor.c: + * validate/gst/validate/gst-validate-element-monitor.c: + * validate/gst/validate/gst-validate-monitor-factory.c: + * validate/gst/validate/gst-validate-monitor-preload.c: + * validate/gst/validate/gst-validate-monitor.c: + * validate/gst/validate/gst-validate-override-registry.c: + * validate/gst/validate/gst-validate-override.c: + * validate/gst/validate/gst-validate-pad-monitor.c: + * validate/gst/validate/gst-validate-reporter.c: + * validate/gst/validate/gst-validate-runner.c: + validade: add missing config.h includes + +2013-08-14 19:14:18 -0300 Thiago Santos + + * validate/gst/validate/Makefile.am: + * validate/gst/validate/gst-validate-file-check.c: + * validate/gst/validate/gst-validate-monitor-preload.c: + * validate/gst/validate/gst-validate-runner.c: + * validate/gst/validate/gst-validate-transcoding.c: + * validate/gst/validate/gst-validate.c: + * validate/gst/validate/validate.c: + * validate/gst/validate/validate.h: + validate: add init function + Adds an init() function that should be called before using the lib. + It takes care of calling all internal initializing functions in + gst-validete + +2013-08-14 18:04:23 -0300 Thiago Santos + + * validate/gst/validate/gst-validate-file-check.c: + * validate/gst/validate/gst-validate-transcoding.c: + * validate/gst/validate/gst-validate.c: + tools: improve documentation + +2013-08-14 16:30:39 -0300 Thiago Santos + + * validate/autogen.sh: + * validate/configure.ac: + * validate/gst/Makefile.am: + * validate/gst/qa/.gitignore: + * validate/gst/qa/Makefile.am: + * validate/gst/qa/gettext.h: + * validate/gst/qa/gst-qa-bin-monitor.c: + * validate/gst/qa/gst-qa-bin-monitor.h: + * validate/gst/qa/gst-qa-default-overrides.c: + * validate/gst/qa/gst-qa-element-monitor.c: + * validate/gst/qa/gst-qa-element-monitor.h: + * validate/gst/qa/gst-qa-file-check.c: + * validate/gst/qa/gst-qa-file-checker.c: + * validate/gst/qa/gst-qa-file-checker.h: + * validate/gst/qa/gst-qa-i18n-lib.h: + * validate/gst/qa/gst-qa-monitor-factory.c: + * validate/gst/qa/gst-qa-monitor-factory.h: + * validate/gst/qa/gst-qa-monitor-preload.c: + * validate/gst/qa/gst-qa-monitor.c: + * validate/gst/qa/gst-qa-monitor.h: + * validate/gst/qa/gst-qa-override-registry.c: + * validate/gst/qa/gst-qa-override-registry.h: + * validate/gst/qa/gst-qa-override.c: + * validate/gst/qa/gst-qa-override.h: + * validate/gst/qa/gst-qa-pad-monitor.c: + * validate/gst/qa/gst-qa-pad-monitor.h: + * validate/gst/qa/gst-qa-report.c: + * validate/gst/qa/gst-qa-report.h: + * validate/gst/qa/gst-qa-reporter.c: + * validate/gst/qa/gst-qa-reporter.h: + * validate/gst/qa/gst-qa-runner.c: + * validate/gst/qa/gst-qa-runner.h: + * validate/gst/qa/gst-qa-scenario.c: + * validate/gst/qa/gst-qa-scenario.h: + * validate/gst/qa/gst-qa-transcoding.c: + * validate/gst/qa/gst-qa.c: + * validate/gst/qa/qa.h: + * validate/gst/validate/.gitignore: + * validate/gst/validate/Makefile.am: + * validate/gst/validate/gettext.h: + * validate/gst/validate/gst-validate-bin-monitor.c: + * validate/gst/validate/gst-validate-bin-monitor.h: + * validate/gst/validate/gst-validate-default-overrides.c: + * validate/gst/validate/gst-validate-element-monitor.c: + * validate/gst/validate/gst-validate-element-monitor.h: + * validate/gst/validate/gst-validate-file-check.c: + * validate/gst/validate/gst-validate-file-checker.c: + * validate/gst/validate/gst-validate-file-checker.h: + * validate/gst/validate/gst-validate-i18n-lib.h: + * validate/gst/validate/gst-validate-monitor-factory.c: + * validate/gst/validate/gst-validate-monitor-factory.h: + * validate/gst/validate/gst-validate-monitor-preload.c: + * validate/gst/validate/gst-validate-monitor.c: + * validate/gst/validate/gst-validate-monitor.h: + * validate/gst/validate/gst-validate-override-registry.c: + * validate/gst/validate/gst-validate-override-registry.h: + * validate/gst/validate/gst-validate-override.c: + * validate/gst/validate/gst-validate-override.h: + * validate/gst/validate/gst-validate-pad-monitor.c: + * validate/gst/validate/gst-validate-pad-monitor.h: + * validate/gst/validate/gst-validate-report.c: + * validate/gst/validate/gst-validate-report.h: + * validate/gst/validate/gst-validate-reporter.c: + * validate/gst/validate/gst-validate-reporter.h: + * validate/gst/validate/gst-validate-runner.c: + * validate/gst/validate/gst-validate-runner.h: + * validate/gst/validate/gst-validate-scenario.c: + * validate/gst/validate/gst-validate-scenario.h: + * validate/gst/validate/gst-validate-transcoding.c: + * validate/gst/validate/gst-validate.c: + * validate/gst/validate/validate.h: + rename gst-qa -> gst-validate + +2013-08-14 15:58:34 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-pad-monitor.c: + pad-monitor: only do complete caps checks on setcaps + On get caps it is acceptable to have missing fields to simplify caps + negotiation + +2013-08-13 13:40:48 -0300 Thiago Santos + + * validate/gst/qa/Makefile.am: + qa-preload: split to separate lib + It should only be used separately, otherwise it will wrap around any + pipeline from applications linking with gstqa + +2013-08-12 15:18:36 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-file-check.c: + * validate/gst/qa/gst-qa-file-checker.c: + * validate/gst/qa/gst-qa-file-checker.h: + file-check: add reverse-playback test + Adds a test that checks if reverse playback works without errors + +2013-08-13 11:07:31 +0200 Edward Hervey + + * validate/gst/qa/gst-qa-reporter.c: + qa-reporter: Make debug message a bit more readable + By surrounding it with double quotes + +2013-08-13 11:07:05 +0200 Edward Hervey + + * validate/gst/qa/gst-qa-pad-monitor.c: + pad-monitor: 0.10 uses "channel-positions" field in audio caps + And it's an array, not a string + +2013-08-13 10:11:42 +0200 Edward Hervey + + * validate/Makefile.am: + * validate/gst/qa/Makefile.am: + Makefile: Clean up for make distcheck + Directories, headers, files weren't properly disted + Also clean up the various CFLAGS/HEADERS/SOURCES variables and remove + ones that aren't needed. + +2013-08-13 09:44:50 +0200 Edward Hervey + + * validate/po/Makevars: + po: Add missing Makevars file + +2013-08-09 12:37:49 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-pad-monitor.c: + pad-monitor: use correct variable for segment comparisons + Use the expected versus the received instead of using the received + twice. + +2013-08-09 12:33:27 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-reporter.c: + reporter: fix printf format type + +2013-08-08 12:35:50 -0300 Thiago Santos + + * validate/README: + * validate/docs/qa-design.txt: + * validate/docs/qa-usage.txt: + docs: improve and update docs + +2013-08-07 17:31:17 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-bin-monitor.c: + * validate/gst/qa/gst-qa-bin-monitor.h: + * validate/gst/qa/gst-qa-runner.c: + * validate/gst/qa/gst-qa-runner.h: + qa-scenario: re add scenarios creation to bin-monitor + GstPipelines are monitored by bin monitors. Create scenarios if + requested from the bin monitors and store them there. + +2013-08-07 16:22:36 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-monitor.c: + * validate/gst/qa/gst-qa-monitor.h: + * validate/gst/qa/gst-qa-reporter.h: + qa-monitor: remove reference to the runner + qa-monitor implements qa-reporter, and we already have a runner stored + there. + +2013-08-07 16:13:33 -0300 Thiago Santos + + * validate/gst/qa/.gitignore: + gitignore: ignore more binaries + +2013-08-07 16:12:45 -0300 Thiago Santos + + * validate/gst/qa/Makefile.am: + * validate/gst/qa/gst-qa-file-check.c: + qa-file-check: add new binary to run file checks easily + It creates a GstQaFileChecker and runs it on the passed URI with + the tests enabled as arguments + +2013-08-07 16:10:57 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-monitor-preload.c: + * validate/gst/qa/gst-qa-runner.c: + * validate/gst/qa/gst-qa-runner.h: + * validate/gst/qa/gst-qa-scenario.c: + * validate/gst/qa/gst-qa-scenario.h: + * validate/gst/qa/gst-qa-transcoding.c: + * validate/gst/qa/gst-qa.c: + * validate/gst/qa/qa.h: + qa-runner: simplify runner to not hold refs to monitor/pipeline + The GstQaRunner is now a simple aggregator of reports that it receives + from monitors and filechecker. This allows it to be used in both + scenarios without APIs that expect GstElement or Monitors, that are + only used on the pipeline monitoring QA tests. + +2013-08-07 11:31:04 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-file-checker.c: + * validate/gst/qa/gst-qa-file-checker.h: + * validate/gst/qa/gst-qa-report.c: + * validate/gst/qa/gst-qa-report.h: + * validate/gst/qa/gst-qa-transcoding.c: + file-checker: add file playback testing feature + Adds a property that triggers the file playback tests on + GstQaFileCheker. Also enable it in the gst-transcoding post file checks. + The implementation is simple, just create a playbin2 and use fakesinks + as sinks, set it to playing and wait for either EOS or ERROR messages. + +2013-08-06 19:42:21 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-reporter.h: + qa-reporter: fix typo + +2013-08-06 19:39:58 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-file-checker.c: + file-checker: include restriction caps tests when checking for profiles + Also move the caps check earlier on the path, to error out sooner and + avoid iterating the sub streams without needing + +2013-08-06 18:17:39 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-file-checker.c: + file-checker: replace encoding profile comparison + Use our own custom comparison to allow to add more fine grained error + reporting. Also the encoding profile is_equal function is too strict as + it also compares profiles names, that doesn't matter to us. + This commit implementation is still initial and needs improvements as it + isn't using the restriction caps, which includes information that might not be + on the profile format caps. + +2013-08-06 10:36:58 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-reporter.c: + qa-reporter: fix crash by avoiding unref an integer + +2013-08-06 10:36:47 -0300 Thiago Santos + + * validate/gst/qa/Makefile.am: + makefile: fix build of gst-qa- tools + +2013-08-06 10:36:02 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-file-checker.c: + * validate/gst/qa/gst-qa-report.c: + * validate/gst/qa/gst-qa-report.h: + file-checker: add error report and new report types + Add a list of new report types and use them in the file-checker. + The errors are mostly related to testing file attributes against + expected values + +2013-08-05 14:16:06 -0300 Thiago Santos + + * validate/gst/qa/Makefile.am: + * validate/gst/qa/gst-qa-file-checker.c: + * validate/gst/qa/gst-qa-file-checker.h: + * validate/gst/qa/gst-qa-transcoding.c: + qa-file-checker: add a file checker object/runner + It is an object that is capable to run a few file checks. The + implemented tests are: file size, duration, if the file is seekable and + comparing the file stream types with a encoding profile + +2013-08-01 18:08:44 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-scenario.c: + qa-scenario: adding eos scenario action + Allows sending EOS to the pipeline + +2013-08-01 09:35:59 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-report.c: + * validate/gst/qa/gst-qa-report.h: + * validate/gst/qa/gst-qa-scenario.c: + qa-scenario: add new scenario action - Pause + The pause action instructs the pipeline to go to paused state and then + return to playing. It has the argument 'duration', that indicates the + duration for which the pipeline will remain in paused + +2013-08-01 01:27:20 -0300 Thiago Santos + + * validate/data/simple_seeks.xml: + * validate/gst/qa/gst-qa-scenario.c: + qa-scenario: refactor to accomodate more actions + Refactor to be able to reuse to add more actions to scenarios. + Planned are pauses and encoding changes + +2013-07-31 15:01:13 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-scenario.c: + qa-scenario: avoid assertion on dispose + After an error, the pipeline might still be null, check before unreffing + +2013-07-31 15:00:56 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-scenario.c: + qa-scenario: fix typo on define variable + +2013-07-31 15:00:33 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-report.c: + qa-report: fix typo on assertion comparison + +2013-07-31 11:12:41 +0100 Vincent Penquerc'h + + * validate/gst/qa/gst-qa-element-monitor.c: + gst-qa-element-monitor: do not bypass monitor factory + A pad monitor was created directly. Prefer going through the + factory. + +2013-07-31 11:05:05 +0100 Vincent Penquerc'h + + * validate/gst/qa/gst-qa-report.c: + gst-qa-reporter: fix use of uninitialized repeat field + +2013-07-31 11:04:32 +0100 Vincent Penquerc'h + + * validate/gst/qa/gst-qa-reporter.c: + gst-qa-reporter: fix report leak when discarding repeated report + +2013-07-31 10:49:48 +0100 Vincent Penquerc'h + + * validate/autogen.sh: + * validate/configure.ac: + * validate/po/POTFILES.in: + gst-qa: fix build in po + Using a lot of grep and some cargo culting. + +2013-07-31 10:07:53 +0100 Vincent Penquerc'h + + * validate/gst/qa/Makefile.am: + gst-qa: make tools depend on libraries + This fixes parallel build randomly breaking. + +2013-07-30 17:07:13 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-report.c: + * validate/gst/qa/gst-qa-report.h: + qa-report: expose API for adding custom issues + expose gst_qa_issue_register and gst_qa_issue_new to allow applications + to register their own custom issues. + Issues IDs should use Areas higher than GST_QA_AREA_OTHER for custom + areas. And to add more issues to existing areas, the IDs should be + higher than GST_QA_ISSUE_ID_CUSTOM_FIRST. + Custom issues registering should be done at startup and from the same + thread as there is no locking around the issues hashtable + +2013-07-30 16:21:15 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-transcoding.c: + * validate/gst/qa/gst-qa.c: + Fix typos + +2013-07-30 16:20:49 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-runner.c: + * validate/gst/qa/gst-qa-runner.h: + * validate/gst/qa/gst-qa-transcoding.c: + * validate/gst/qa/gst-qa.c: + qa-runner: Remove printing API from qa-runner + Replace it with functions to list the reports + +2013-07-30 12:17:48 -0400 Vincent Penquerc'h + + * validate/gst/qa/gst-qa-pad-monitor.c: + pad-monitor: fix NULL format string + An empty message should be an empty string. + +2013-07-30 10:21:13 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-monitor-preload.c: + * validate/gst/qa/gst-qa-runner.c: + * validate/gst/qa/gst-qa-runner.h: + * validate/gst/qa/gst-qa-transcoding.c: + * validate/gst/qa/gst-qa.c: + qa-runner: removing _setup call + Do setup on the _new function directly instead of having a separate + call for that + +2013-07-30 09:56:05 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-override.c: + * validate/gst/qa/gst-qa-override.h: + * validate/gst/qa/gst-qa-pad-monitor.c: + qa-override: add more pad overrides for buffer probe and caps + Add override functions for custom checking of buffer probe and + getcaps/setcaps functions. + +2013-07-29 17:26:21 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-monitor.c: + * validate/gst/qa/gst-qa-monitor.h: + * validate/gst/qa/gst-qa-override.c: + * validate/gst/qa/gst-qa-override.h: + * validate/gst/qa/gst-qa-pad-monitor.c: + pad-monitor: call the event/query/buffer overrides + Use the new event/buffer/query overrides to allow custom checks + on those scenarios + +2013-07-29 16:26:52 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-override.c: + * validate/gst/qa/gst-qa-override.h: + qa-override: add callbacks for query/buffer/event functions + Add callbacks for pad event/buffer/query functions in case the + override wants to do additional checks + +2013-07-30 10:20:43 +0100 Vincent Penquerc'h + + * validate/gst/qa/Makefile.am: + * validate/gst/qa/gst-qa-default-overrides.c: + * validate/gst/qa/gst-qa-override-registry.c: + * validate/gst/qa/gst-qa-override-registry.h: + * validate/gst/qa/gst-qa-runner.c: + gst-qa-override-registry: load overrides dynamically + Shared objects listed in GST_QA_OVERRIDE are loaded on startup, + and the symbol gst_qa_create_overrides is run. It should create + any override needed. While it can do anything it wants, this + is discouraged. + GST_QA_OVERRIDE should be a comma separated list of shared objects, + any relative paths should be from the current working directory + at the time they are loaded (ie, if the process to be traced + changes cwd, use absolute paths). + No attempt whatsoever is made at not running what was not meant. + Includes a sample shared object for illustration purposes. + +2013-07-29 13:17:50 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-monitor.c: + * validate/gst/qa/gst-qa-monitor.h: + * validate/gst/qa/gst-qa-override-registry.c: + * validate/gst/qa/gst-qa-override-registry.h: + qa-override-registry: register overrides by gtype and klass + Overrides can now be registerd by gtype, meaning that they will + be attached to monitors that the target is of the requested type. + Also by element klass, that will check that the element has the + selected class in its details + +2013-07-29 12:01:02 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-monitor.c: + * validate/gst/qa/gst-qa-monitor.h: + qa-monitor: implement intercept_report + It is used to iterate over overrides and modify the report level if + the overrides wants to do so. + Also adds a new mutex only for the overrides to avoid deadlocks when + reporting if we used the same lock for iterating the overrides + +2013-07-29 11:35:20 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-reporter.h: + qa-reporter: fix copy n paste left over + +2013-07-29 11:34:42 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-report.c: + * validate/gst/qa/gst-qa-report.h: + * validate/gst/qa/gst-qa-reporter.c: + * validate/gst/qa/gst-qa-reporter.h: + qa-reporter: add function for intercepting reports + after report creation, this function is called and implementers can + modify the report to their liking before it is posted to the runner + +2013-07-29 09:37:46 -0400 Vincent Penquerc'h + + * validate/gst/qa/gst-qa-monitor.c: + qa-monitor: chain gst_qa_monitor_finalize to parent's finalize + It was chaining to the parent's dispose. + +2013-07-29 10:06:48 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-report.h: + gst-qa-report: put the correct format to avoid compiler warnings + +2013-07-29 07:02:30 -0400 Vincent Penquerc'h + + * validate/gst/qa/Makefile.am: + * validate/gst/qa/gettext.h: + * validate/gst/qa/gst-qa-i18n-lib.h: + * validate/gst/qa/gst-qa-report.c: + i18n: copy necessary files in-tree + +2013-07-29 07:20:50 -0400 Vincent Penquerc'h + + * validate/gst/qa/gst-qa-report.h: + GstIssueId: make this uintptr_t + As it's used a a placeholder pointer for g_hash_table use, + it needs to be converted back and forth to a pointer. + +2013-07-26 19:05:31 -0300 Thiago Santos + + * validate/gst/qa/Makefile.am: + * validate/gst/qa/gst-qa-bin-monitor.c: + * validate/gst/qa/gst-qa-element-monitor.c: + * validate/gst/qa/gst-qa-monitor-factory.c: + * validate/gst/qa/gst-qa-monitor-factory.h: + * validate/gst/qa/gst-qa-monitor.c: + * validate/gst/qa/gst-qa-monitor.h: + * validate/gst/qa/gst-qa-override-registry.c: + * validate/gst/qa/gst-qa-override-registry.h: + * validate/gst/qa/gst-qa-pad-monitor.c: + * validate/gst/qa/gst-qa-pad-monitor.h: + * validate/gst/qa/gst-qa-runner.c: + * validate/gst/qa/gst-qa-runner.h: + gst-qa-override-registry: adding the override-registry + This registry should contain the list of GstQaOverride to + be used on the pipelines being monitored + +2013-07-26 00:14:02 -0300 Thiago Santos + + * validate/gst/qa/Makefile.am: + * validate/gst/qa/gst-qa-override.c: + * validate/gst/qa/gst-qa-override.h: + qa-override: adds qa-override that can change the report level of issues + Useful for customizing the level of issues for particular elements/tests + when they are more relevant or have to be disabled + +2013-07-25 23:25:22 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-pad-monitor.c: + * validate/gst/qa/gst-qa-report.c: + * validate/gst/qa/gst-qa-report.h: + * validate/gst/qa/gst-qa-reporter.c: + * validate/gst/qa/gst-qa-reporter.h: + * validate/gst/qa/gst-qa-scenario.c: + qa-report: splitting a GstQaReport into a GstQaIssue and GstQaReport + Reports now point to Issues, that are uniquely identified and have + translatable descriptions. This way we are going to be able to uniquely + identify the issues and applications can enable/disable checks for + specific elements. + +2013-07-24 19:09:14 -0400 Thibault Saunier + + * validate/gst/qa/gst-qa-transcoding.c: + * validate/gst/qa/gst-qa.c: + qa: Make it possible to set a scenario from the command line in test apps + +2013-07-23 10:13:06 -0400 Thibault Saunier + + * validate/gst/qa/gst-qa-monitor.c: + * validate/gst/qa/gst-qa-reporter.c: + * validate/gst/qa/gst-qa-reporter.h: + * validate/gst/qa/gst-qa-scenario.c: + qa: Properly set reporter's runner reference + That was never set, but it is needed for the reporter to properly add + reports to the runner. + We still keep a reference on the monitor to make things simpler + +2013-07-23 08:55:24 -0400 Thibault Saunier + + * validate/gst/qa/gst-qa-reporter.c: + * validate/gst/qa/gst-qa-reporter.h: + reporter: Use Gst debugging log in the _report method directly + Using __VALIST__ was not properly working + + Add a gstqareporter debug category + +2013-07-22 19:22:49 -0400 Thibault Saunier + + * validate/gst/qa/gst-qa-report.c: + * validate/gst/qa/gst-qa-report.h: + * validate/gst/qa/gst-qa-reporter.c: + * validate/gst/qa/gst-qa-reporter.h: + * validate/gst/qa/gst-qa-runner.c: + * validate/gst/qa/gst-qa-scenario.c: + * validate/gst/qa/gst-qa-scenario.h: + scenario: Implement the GstQaReporter interface and make use of it + This way we can report issues from a scenario + Also add a Seek aread to the known areas list + We now need to pass the runner to the scenario instead of the + pipeline as the GstQaReporter interface needs it. + +2013-07-22 19:17:53 -0400 Thibault Saunier + + * validate/data/Makefile.am: + * validate/gst/qa/Makefile.am: + * validate/gst/qa/gst-qa-monitor.c: + * validate/gst/qa/gst-qa-monitor.h: + * validate/gst/qa/gst-qa-pad-monitor.c: + * validate/gst/qa/gst-qa-report.c: + * validate/gst/qa/gst-qa-report.h: + * validate/gst/qa/gst-qa-reporter.c: + * validate/gst/qa/gst-qa-reporter.h: + qa: Add a GstQaReporter interface that objects needing reporting can implement + Various type of object should be able to do some reporting, so we have + to make sure all the code to do that is in one place. Creating an interface + makes it simple to share information and it avoid to have a baseclass for + something that is not actually important enough to create a baseclass. + Conflicts: + gst/qa/gst-qa-pad-monitor.c + +2013-07-20 00:18:13 -0400 Thibault Saunier + + * validate/configure.ac: + * validate/data/Makefile.am: + * validate/data/simple_seeks.xml: + * validate/gst/qa/Makefile.am: + * validate/gst/qa/gst-qa-runner.c: + * validate/gst/qa/gst-qa-runner.h: + * validate/gst/qa/gst-qa-scenario.c: + * validate/gst/qa/gst-qa-scenario.h: + qa: Add a GstQaScenario class making it possible to execute scenarios + A scenario correspond to a suite of action to execute on a pipeline, + for the time being, we only support seeking the pipeline, but in the + future we can imagine doing some queries, setting pipeline state, etc... + The scenario can be loaded thanks to the GST_QA_SCENARIO environment + variable, making it usable with any existant application, in case, the + application can be used interactively, the user should either, not load + any scenario or let the application run without interacting with it. + +2013-07-24 16:04:03 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-pad-monitor.c: + pad-monitor: add lots of locking + When handling elements that spawn multiple threads (hardware + enc/decoders), the pad monitor has to protect its variables specially + because some checks involve iterating over internally linked pads to + add/get some data for comparison (expected events, timestamp ranges, + caps). + Aside from locking its own mutex, the pad monitor can also lock the + parent's mutex when it needs to use data from its internally linked + pads. The locking order should always be parent and then individual + pad-monitor mutexes. This should prevent deadlocks when multiple + pad-monitors from the same element start doing checks at the same time + from different threads. + +2013-07-24 10:05:31 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-pad-monitor.c: + pad-monitor: remove already solved TODOs + +2013-07-24 09:51:05 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-pad-monitor.c: + * validate/gst/qa/gst-qa-pad-monitor.h: + pad-monitor: accept unexpected flow return if pad is eos + Track eos event and mark that pad as eos so that checking for the + flow return knows when 'unexpected' is acceptable + +2013-07-23 15:18:51 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-pad-monitor.c: + pad-monitor: improve caps proxying check on getcaps + Only check if fields are proxied for sink getcaps as it is when + downstream restrictions should be proxied. Also improve the + fields comparison to handle single value x multi value + (list/array/range) contain relations. + +2013-07-23 15:10:33 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-pad-monitor.c: + pad-monitor: improve type conformance checking for caps + Replace the macro with a more powerful variadic function that can + check for more acceptable types for the same caps. + This removes a few more false positives + +2013-07-23 12:52:22 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-pad-monitor.c: + pad-monitor: only expect a new segment if pad is running on push mode + For pull mode, it should just provide the buffers, regardless of getting + a new segment or not + +2013-07-23 12:14:26 -0300 Edward Hervey + + * validate/gst/qa/gst-qa.c: + gst-qa: show help and exit when no arguments are provided + Instead of attempting to create empty pipelines and weird things + happening :) + +2013-07-23 12:11:08 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-pad-monitor.c: + pad-monitor: fix flushes checking + flush events shouldn't fail, so we don't need to rollback when it + returns false from downstream (this is common when downstream is still + not-linked) and it would cause gst-qa to spit false positives. + Also refactor the common event handling for both sink and src event + functions into a common place. Currently we handle flushes the same + for both pad's directions + +2013-07-23 11:51:07 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-pad-monitor.c: + pad-monitor: only merge caps if they exist + Downtream can not be linked, so we shouldn't try to merge + NULL caps + +2013-07-22 20:50:02 -0300 Thiago Santos + + * validate/configure.ac: + configure: add nano version to enable Werror + +2013-07-22 20:09:35 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-pad-monitor.c: + pad-monitor: fix expected setcaps fields comparison + Use the correct structure when getting the GValues and print different + messages for missing and different fields on the setcaps caps + +2013-07-22 20:09:07 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-pad-monitor.c: + pad-monitor: improve timestamp ranges comparison message a little + Show the buffer range that is being compared. + +2013-07-22 15:05:04 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-pad-monitor.c: + * validate/gst/qa/gst-qa-pad-monitor.h: + pad-monitor: add check for serialized events order + Store expected serialized events and their 'timestamps' to check if + they are pushed in the same order/time as they were received + +2013-07-22 09:50:23 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-pad-monitor.c: + * validate/gst/qa/gst-qa-pad-monitor.h: + pad-monitor: add check for setcaps passing audio/video fields + Checks that the common audio/video fields are correctly passed + downstream after a setcaps + +2013-07-19 16:52:45 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-pad-monitor.c: + * validate/gst/qa/gst-qa-report.h: + pad-monitor: add check for getcaps proxying audio/video fields + Checks that the common audio/video fields are correctly proxied by + the elements after a getcaps + +2013-07-19 16:52:11 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-element-monitor.c: + * validate/gst/qa/gst-qa-element-monitor.h: + element-monitor: add is_encoder flag + Easy access to knowing if the monitored element is an encoder + +2013-07-18 16:53:46 -0400 Thibault Saunier + + * validate/configure.ac: + * validate/gst/qa/Makefile.am: + * validate/gst/qa/gst-qa-transcoding.c: + qa-transcoding: Add a binary program to easily test transcoding + +2013-07-18 18:20:09 -0400 Thibault Saunier + + * validate/gst/qa/gst-qa-bin-monitor.c: + * validate/gst/qa/gst-qa-element-monitor.c: + * validate/gst/qa/gst-qa-pad-monitor.c: + monitor(s): Avoid trying to disconnect handlers on instances that do not exist anymore + +2013-07-18 17:49:44 -0400 Thibault Saunier + + * validate/gst/qa/gst-qa-monitor.c: + * validate/gst/qa/gst-qa-monitor.h: + * validate/gst/qa/gst-qa-pad-monitor.c: + * validate/gst/qa/gst-qa-report.c: + * validate/gst/qa/gst-qa-report.h: + qa-report: Avoid reporting tons of times the exact same issue to users + Some of the issue can be reported once and for all. We are here avoiding to flood the + user with the same information repeated infinitely. + +2013-07-18 13:59:11 -0400 Thibault Saunier + + * validate/gst/qa/gst-qa-pad-monitor.c: + qa-pad-monitor: Do not use gst_private.h + +2013-07-19 11:14:39 -0300 Thiago Santos + + * validate/docs/qa-design.txt: + * validate/docs/qa-usage.txt: + docs: add design and usage docs + +2013-07-19 09:57:07 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-pad-monitor.c: + * validate/gst/qa/gst-qa-pad-monitor.h: + pad-monitor: improve timestamp ranges check + Keep the full range stored by the element in the monitor and check + if outgoing timestamps are within that range. It is simple and + should generally work. + +2013-07-18 14:49:23 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-pad-monitor.c: + pad-monitor: fix combined flow checks + We can only check if we found a downstream monitor + +2013-07-18 14:49:01 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-pad-monitor.c: + pad-monitor: fix caps field type checks + The type is GstValueList and not GArray + +2013-07-18 14:48:46 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-pad-monitor.c: + pad-monitor: improve report messages with arguments + +2013-07-18 12:11:00 -0400 Thibault Saunier + + * validate/gst/qa/gst-qa-monitor.c: + * validate/gst/qa/gst-qa-report.c: + * validate/gst/qa/gst-qa-report.h: + qa-report: Pass the whole monitor when creating a report + So we have the proper source name already avalaible and in the future + we might need some more informations about the monitor itself. + +2013-07-18 12:00:29 -0400 Thibault Saunier + + * validate/gst/qa/gst-qa-pad-monitor.c: + qa-pad-monitor: Properly set target_name with as much info as possible + +2013-07-18 11:49:54 -0400 Thibault Saunier + + * validate/gst/qa/gst-qa-monitor.c: + * validate/gst/qa/gst-qa-monitor.h: + qa-monitor: Add a target name field that can be used even when the target is freed + +2013-07-18 11:49:25 -0400 Thibault Saunier + + * validate/gst/qa/gst-qa-monitor.c: + qa-monitor: Make the reference to the target a weak reference + +2013-07-18 12:09:13 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-report.c: + * validate/gst/qa/gst-qa-report.h: + qa-report: add debug flags for criticals + Allows the user to enable program abort if a report + is created with a certain level. + Use: + GST_QA=fatal_criticals,fatal_warnings,fatal_issues + +2013-07-18 10:59:11 -0400 Thibault Saunier + + * validate/gst/qa/gst-qa-report.c: + * validate/gst/qa/gst-qa-report.h: + qa-report: Do not keep a ref to the source but keep its name instead + We currently do not need to access the object source after its creation + but we need to be able to have a usefull for debugging name. + +2013-07-17 20:21:53 -0400 Thibault Saunier + + * validate/gst/qa/gst-qa-runner.c: + qa-runner: Add a 'report-added' signal + So it is possible to plug into the runner to get information about + what is going from outside of it. + +2013-07-17 19:56:52 -0400 Thibault Saunier + + * validate/gst/qa/gst-qa-monitor.c: + * validate/gst/qa/gst-qa-report.c: + * validate/gst/qa/gst-qa-report.h: + * validate/gst/qa/gst-qa-runner.c: + qa-report: Make it a boxed type + And make it refcounted, in 1.0 it should become a GstMiniObject, for + now, it is enough that way. + The goal is to be able to use it in signals + +2013-07-17 19:18:49 -0400 Thibault Saunier + + * validate/gst/qa/gst-qa-monitor.c: + * validate/gst/qa/gst-qa-monitor.h: + qa-monitor: Allow detaill message to be in printf format + So we can give proper informations about what is wrong to users + +2013-07-17 21:46:37 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-monitor-preload.c: + qa-monitor-preload: fix preload to work with pipeline creation + Wrap around the main gstreamer pipeline creation functions as wrapping + g_object_new requires rebuilding glib. + +2013-07-17 20:40:50 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-pad-monitor.c: + pad-monitor: fix copy n paste mistake + Do not use GstFlowReturn where a boolean is expected + +2013-07-17 20:40:38 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-pad-monitor.c: + pad-monitor: fix initialization of timestamp ranges + +2013-07-17 17:57:39 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-pad-monitor.c: + * validate/gst/qa/gst-qa-pad-monitor.h: + pad-monitor: check that returns are combined properly + When getting a return from a sink pad, check that it combines properly + the current returns from downstream source pads + +2013-07-17 14:36:44 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-pad-monitor.c: + * validate/gst/qa/gst-qa-pad-monitor.h: + pad-monitor: verify that pushed segment matches what was received + Check that src pads push segments that are compatible with what + was received on the sink pads + +2013-07-17 11:31:38 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-pad-monitor.c: + * validate/gst/qa/gst-qa-pad-monitor.h: + pad-monitor: output timestamps should be in range of received ones + Checks if the timestamps of pushed buffers are in the range of the + received buffer timestamps; + +2013-07-17 00:33:42 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-pad-monitor.c: + pad-monitor: add check for out of segment buffers + +2013-07-17 00:30:21 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-pad-monitor.c: + * validate/gst/qa/gst-qa-pad-monitor.h: + pad-monitor: track current buffer timestamp and duration + This can be used to make sure outgoing buffers match the input + timestamps + +2013-07-17 00:29:38 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-pad-monitor.c: + pad-monitor: check for when a flush start isn't expected + Complain when an unexpected flush-start is received + +2013-07-17 00:29:04 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-pad-monitor.c: + * validate/gst/qa/gst-qa-report.h: + pad-monitor: add checks for raw caps completeness + Check audio and video raw caps returned from getcaps for expected + fields and types + +2013-07-17 00:25:11 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-monitor.h: + qa-monitor: Fix typo in printf format for report debug messages + Stringify the arguments correctly for printing + +2013-07-16 23:19:13 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-pad-monitor.c: + * validate/gst/qa/gst-qa-pad-monitor.h: + pad-monitor: first buffer checks + Check that a newsegment is received before the first buffer and that + the first buffer running time is 0 + +2013-07-16 21:15:09 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-monitor.c: + * validate/gst/qa/gst-qa-monitor.h: + * validate/gst/qa/gst-qa-pad-monitor.c: + * validate/gst/qa/gst-qa-report.c: + * validate/gst/qa/gst-qa-report.h: + * validate/gst/qa/gst-qa-runner.c: + * validate/gst/qa/gst-qa-runner.h: + * validate/gst/qa/gst-qa.c: + qa-report: rework qa-report API + Remove error from GstQaErrorReport, making it only GstQaReport. Add + a level and use area and subarea code, with an extra string for message + adding details. + Provide macros on qa-monitor to make it easy to create reports. + +2013-07-16 09:17:44 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-pad-monitor.c: + * validate/gst/qa/gst-qa-pad-monitor.h: + pad-monitor: add stubs for getcaps/setcaps function wrapping + +2013-07-16 08:06:27 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-element-monitor.h: + * validate/gst/qa/gst-qa-pad-monitor.c: + pad-monitor: add check for out of segment buffer data + +2013-07-15 10:15:06 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-report.c: + * validate/gst/qa/gst-qa-report.h: + * validate/gst/qa/gst-qa-runner.c: + qa-report: use gst_util_get_timestamp for report times + Makes it more aligned with GST_DEBUG output + +2013-07-15 09:27:34 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-pad-monitor.c: + pad-monitor: split event checks for src and sink pads + Keeping those handlers separate should keep the code smaller and + easier to understand + +2013-07-12 16:02:25 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-bin-monitor.c: + * validate/gst/qa/gst-qa-bin-monitor.h: + * validate/gst/qa/gst-qa-element-monitor.c: + * validate/gst/qa/gst-qa-element-monitor.h: + * validate/gst/qa/gst-qa-monitor-factory.c: + * validate/gst/qa/gst-qa-monitor-factory.h: + * validate/gst/qa/gst-qa-monitor.c: + * validate/gst/qa/gst-qa-monitor.h: + * validate/gst/qa/gst-qa-pad-monitor.c: + * validate/gst/qa/gst-qa-pad-monitor.h: + * validate/gst/qa/gst-qa-runner.c: + qa-monitor: add parent relation for monitors + This is useful because Pad monitors will have to ask the + parent element monitors for some element details for + doing checks + +2013-07-12 15:42:56 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-element-monitor.c: + * validate/gst/qa/gst-qa-element-monitor.h: + qa-element-monitor: check if the element is a decoder + This can be used on checks for timestamps being inside segment + +2013-07-12 14:18:22 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-pad-monitor.c: + * validate/gst/qa/gst-qa-pad-monitor.h: + pad-monitor: add probes for src pads + To be used for further monitoring events and buffers for + src pads + +2013-07-12 13:32:08 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-report.c: + * validate/gst/qa/gst-qa-report.h: + qa-report: add a timestamp to error reports + +2013-07-12 02:10:06 -0300 Thiago Santos + + * validate/gst/qa/Makefile.am: + * validate/gst/qa/gst-qa-monitor.c: + * validate/gst/qa/gst-qa-monitor.h: + * validate/gst/qa/gst-qa-pad-monitor.c: + * validate/gst/qa/gst-qa-report.c: + * validate/gst/qa/gst-qa-report.h: + * validate/gst/qa/gst-qa-runner.c: + * validate/gst/qa/gst-qa-runner.h: + * validate/gst/qa/gst-qa.c: + qa-report: adds qa-report for reporting errors to GstQaRunner + The errors are printed directly to stdout and are accumulated at + GstQaRunner for being printed at the end if requested + +2013-07-12 01:23:48 -0300 Thiago Santos + + * validate/gst/qa/Makefile.am: + * validate/gst/qa/gst-qa-monitor-preload.c: + qa-monitor-preload: add functions to allow ld-preload to wrap pipelines + The preload functions wrap functions that can create pipelines and + attaches a runner to them for monitoring + +2013-07-12 00:41:43 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-bin-monitor.c: + * validate/gst/qa/gst-qa-bin-monitor.h: + * validate/gst/qa/gst-qa-element-monitor.c: + * validate/gst/qa/gst-qa-element-monitor.h: + * validate/gst/qa/gst-qa-monitor-factory.c: + * validate/gst/qa/gst-qa-monitor-factory.h: + * validate/gst/qa/gst-qa-monitor.c: + * validate/gst/qa/gst-qa-monitor.h: + * validate/gst/qa/gst-qa-pad-monitor.c: + * validate/gst/qa/gst-qa-pad-monitor.h: + * validate/gst/qa/gst-qa-runner.c: + * validate/gst/qa/gst-qa-runner.h: + qa-monitor: add runner property + runner stores the GstQaRunner that will receive the error reports + from the monitors + +2013-07-11 13:43:52 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-pad-monitor.c: + * validate/gst/qa/gst-qa-pad-monitor.h: + pad-monitor: make it able to initialize a segment + Do not take the initial format set to TIME too seriously when we + haven't got any newsegment event yet. If it is the first segment + received, switch our internal segment tracker to the event format + +2013-07-11 13:41:25 -0300 Thiago Santos + + * validate/gst/qa/gst-qa.c: + gst-qa: add seek-tests option + The seek-tests does a simple seeking after the pipeline has started + so that seeking checks can be performed by the monitors + +2013-07-11 02:07:41 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-pad-monitor.c: + * validate/gst/qa/gst-qa-pad-monitor.h: + pad-monitor: track some events + Segments, upstream seeks and flushes. Adding the following checks: + * A flush stop is expected after a flush start + * After a seek, the flushes/segment seqnum should be the same as the seek + +2013-07-11 00:05:17 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-pad-monitor.c: + pad-monitor: only set pad functions if they exist on the pad + Some functions should only be set on pads if they were originally + set, like the GetRange, Chain and BufferAlloc + +2013-07-11 00:04:41 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-bin-monitor.c: + * validate/gst/qa/gst-qa-element-monitor.c: + qa-bin-monitor/element-monitor: implement pad/element wrapping + Add code that creates new monitors when elements/pads are found + in bin and element monitors + +2013-07-11 00:03:54 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-bin-monitor.c: + * validate/gst/qa/gst-qa-element-monitor.c: + * validate/gst/qa/gst-qa-monitor.c: + * validate/gst/qa/gst-qa-pad-monitor.c: + qa-monitor: fix various start up issues + Fix reference count for monitored object, passing of constructor + parameter and base monitor property flag + +2013-07-10 18:38:09 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-pad-monitor.c: + * validate/gst/qa/gst-qa-pad-monitor.h: + pad-monitor: instrument to monitor buffer/event/query/alloc flows + Replace pad functions with monitor functions that can do pre/post + checks and call the original functions + +2013-07-10 14:03:49 -0300 Thiago Santos + + * validate/gst/qa/Makefile.am: + * validate/gst/qa/gst-qa-bin-monitor.c: + * validate/gst/qa/gst-qa-bin-monitor.h: + * validate/gst/qa/gst-qa-element-monitor.c: + * validate/gst/qa/gst-qa-element-monitor.h: + * validate/gst/qa/gst-qa-monitor-factory.c: + * validate/gst/qa/gst-qa-monitor.c: + * validate/gst/qa/gst-qa-monitor.h: + * validate/gst/qa/gst-qa-pad-monitor.c: + * validate/gst/qa/gst-qa-pad-monitor.h: + qa-monitor: add base class for monitors + The base class adds a 'object' property to hold the monitored object, + it can only be set on construction. Also the constructor now + automatically calls the element set up + +2013-07-09 19:20:55 -0300 Thiago Santos + + * validate/gst/qa/Makefile.am: + * validate/gst/qa/gst-qa-bin-monitor.c: + * validate/gst/qa/gst-qa-bin-monitor.h: + * validate/gst/qa/gst-qa-element-monitor.c: + * validate/gst/qa/gst-qa-element-monitor.h: + * validate/gst/qa/gst-qa-monitor-factory.c: + qa-bin-monitor: adds a bin monitor + Extends element-monitor to also wrap child elements + +2013-07-09 17:38:47 -0300 Thiago Santos + + * validate/gst/qa/Makefile.am: + * validate/gst/qa/gst-qa-element-monitor.c: + * validate/gst/qa/gst-qa-element-monitor.h: + * validate/gst/qa/gst-qa-element-wrapper.c: + * validate/gst/qa/gst-qa-element-wrapper.h: + * validate/gst/qa/gst-qa-monitor-factory.c: + * validate/gst/qa/gst-qa-monitor-factory.h: + * validate/gst/qa/gst-qa-pad-monitor.c: + * validate/gst/qa/gst-qa-pad-monitor.h: + * validate/gst/qa/gst-qa-pad-wrapper.c: + * validate/gst/qa/gst-qa-pad-wrapper.h: + * validate/gst/qa/gst-qa-runner.c: + * validate/gst/qa/gst-qa-runner.h: + * validate/gst/qa/gst-qa-wrapper-factory.c: + * validate/gst/qa/gst-qa-wrapper-factory.h: + qa: renaming Wrapper -> Monitor + +2013-07-09 16:52:02 -0300 Thiago Santos + + * validate/gst/qa/Makefile.am: + * validate/gst/qa/gst-qa-element-wrapper.c: + * validate/gst/qa/gst-qa-pad-wrapper.c: + * validate/gst/qa/gst-qa-pad-wrapper.h: + * validate/gst/qa/gst-qa-runner.c: + qa-pad-wrapper: adds stub class for pad QA wrapper + Also fixes _new functions to ref the elements intead of + ownership transfers + +2013-07-09 16:39:38 -0300 Thiago Santos + + * validate/gst/qa/gst-qa-element-wrapper.c: + * validate/gst/qa/gst-qa-element-wrapper.h: + qa-element-wrapper: add code for iterating and monitoring pads creation + This will be used to create the wrappers for pads + +2013-07-09 16:13:00 -0300 Thiago Santos + + * validate/gst/qa/.gitignore: + gitignore: ignore gst-qa binary + +2013-07-09 16:08:30 -0300 Thiago Santos + + * validate/AUTHORS: + * validate/COPYING: + * validate/ChangeLog: + * validate/Makefile.am: + * validate/NEWS: + * validate/README: + * validate/autogen.sh: + * validate/configure.ac: + * validate/gst/Makefile.am: + * validate/gst/qa/Makefile.am: + * validate/gst/qa/gst-qa-element-wrapper.c: + * validate/gst/qa/gst-qa-element-wrapper.h: + * validate/gst/qa/gst-qa-runner.c: + * validate/gst/qa/gst-qa-runner.h: + * validate/gst/qa/gst-qa-wrapper-factory.c: + * validate/gst/qa/gst-qa-wrapper-factory.h: + * validate/gst/qa/gst-qa.c: + * validate/gst/qa/qa.h: + qa: adds gst-qa binary and basic classes to run the QA tests + The classes are mostly a stub for now, but the gst-qa already + has a minimum to start them; + +2013-07-09 16:07:58 -0300 Thiago Santos + + * common: + * validate/.gitmodules: + qa: add common submodule + diff --git a/validate/Makefile.am b/validate/Makefile.am new file mode 100644 index 0000000..a035b92 --- /dev/null +++ b/validate/Makefile.am @@ -0,0 +1,79 @@ +DISTCHECK_CONFIGURE_FLAGS=--enable-gtk-doc + +SUBDIRS = \ + common \ + data \ + gst \ + gst-libs \ + plugins \ + launcher \ + tools \ + pkgconfig \ + docs \ + tests \ + po + +DIST_SUBDIRS = $(SUBDIRS) + +suppsdir=${datadir}/gstreamer-$(GST_API_VERSION)/validate/ +supps_DATA = \ + common/gst.supp \ + data/gstvalidate.supp + +EXTRA_DIST = \ + ChangeLog autogen.sh depcomp \ + COPYING \ + common/gst.supp \ + data/gstvalidate.supp + +ACLOCAL_AMFLAGS = -I m4 -I common/m4 + +DISTCLEANFILES = _stdint.h + +include $(top_srcdir)/common/release.mak +include $(top_srcdir)/common/po.mak + +include $(top_srcdir)/common/coverage/lcov.mak + +# cruft: plugins that have been merged or moved or renamed +CRUFT_FILES = \ + $(top_builddir)/common/shave \ + $(top_builddir)/common/shave-libtool \ + $(top_builddir)/common/m4/codeset.m4 \ + $(top_builddir)/common/m4/gettext.m4 \ + $(top_builddir)/common/m4/glibc2.m4 \ + $(top_builddir)/common/m4/glibc21.m4 \ + $(top_builddir)/common/m4/iconv.m4 \ + $(top_builddir)/common/m4/intdiv0.m4 \ + $(top_builddir)/common/m4/intl.m4 \ + $(top_builddir)/common/m4/intldir.m4 \ + $(top_builddir)/common/m4/intlmacosx.m4 \ + $(top_builddir)/common/m4/intmax.m4 \ + $(top_builddir)/common/m4/inttypes-pri.m4 \ + $(top_builddir)/common/m4/inttypes_h.m4 \ + $(top_builddir)/common/m4/lcmessage.m4 \ + $(top_builddir)/common/m4/lib-ld.m4 \ + $(top_builddir)/common/m4/lib-link.m4 \ + $(top_builddir)/common/m4/lib-prefix.m4 \ + $(top_builddir)/common/m4/libtool.m4 \ + $(top_builddir)/common/m4/lock.m4 \ + $(top_builddir)/common/m4/longlong.m4 \ + $(top_builddir)/common/m4/ltoptions.m4 \ + $(top_builddir)/common/m4/ltsugar.m4 \ + $(top_builddir)/common/m4/ltversion.m4 \ + $(top_builddir)/common/m4/lt~obsolete.m4 \ + $(top_builddir)/common/m4/nls.m4 \ + $(top_builddir)/common/m4/po.m4 \ + $(top_builddir)/common/m4/printf-posix.m4 \ + $(top_builddir)/common/m4/progtest.m4 \ + $(top_builddir)/common/m4/size_max.m4 \ + $(top_builddir)/common/m4/stdint_h.m4 \ + $(top_builddir)/common/m4/uintmax_t.m4 \ + $(top_builddir)/common/m4/visibility.m4 \ + $(top_builddir)/common/m4/wchar_t.m4 \ + $(top_builddir)/common/m4/wint_t.m4 \ + $(top_builddir)/common/m4/xsize.m4 + +include $(top_srcdir)/common/cruft.mak + +all-local: check-cruft diff --git a/validate/NEWS b/validate/NEWS new file mode 100644 index 0000000..634f209 --- /dev/null +++ b/validate/NEWS @@ -0,0 +1 @@ +This is the 1.5.2 release of GstValidate diff --git a/validate/README b/validate/README new file mode 100644 index 0000000..fe822ca --- /dev/null +++ b/validate/README @@ -0,0 +1,48 @@ +== Gst-Validate + +The goal of GstValidate is to be able to detect when elements are not +behaving as expected and report it to the user so he knows how things +are supposed to work inside a GstPipeline. In the end, fixing issues +found by the tool will ensure that all elements behave all together in +the expected way. + +The easiest way of using GstValidate is to use one of its command-line +tools, located at tools/ directory. It is also possible to monitor +GstPipelines from any application by using the LD_PRELOAD gstvalidate +lib. The third way of using it is to write your own application that +links and uses libgstvalidate. + +== BUILDING + +Getting the code: + +Releases are available at , download and extract the tarball. If you +want to use latest git version, do: + +git clone + +After cloning or extracting from a tarball, enter the gst-validate directory: + +cd gst-validate + +The 'master' branch uses gstreamer 1.0, there is a '0.10' branch for +gstreamer 0.10. The default is the 'master' branch, if you want to use it +for 0.10, do: + +git checkout --track origin/0.10 + +Build with: + +./autogen.sh --prefix= +make +sudo make install (only if you want to install it) + +Replace with your desired installation path, you can omit +the --prefix argument if you aren't going to install it or if you want the +default /usr/local. It is possible to use gst-validate CLI tools without +installation. + +== INSTRUCTIONS + +If you are looking for informations on how to use gst-validate -> docs/validate-usage.txt +If you are looking for informations on gst-validate design -> docs/validate-design.txt diff --git a/validate/autogen.sh b/validate/autogen.sh new file mode 100755 index 0000000..fbd44a4 --- /dev/null +++ b/validate/autogen.sh @@ -0,0 +1,109 @@ +#!/bin/sh +# +# gst-validate autogen.sh +# +# Run this to generate all the initial makefiles, etc. +# +# This file has been generated from common/autogen.sh.in via common/update-autogen + + +test -n "$srcdir" || srcdir=`dirname "$0"` +test -n "$srcdir" || srcdir=. + +olddir=`pwd` +cd "$srcdir" + +DIE=0 +package=gst-validate +srcfile=gst-validate.doap + +# Make sure we have common +cd ../ +if test ! -f validate/common/gst-autogen.sh; +then + echo "+ Setting up common submodule" + git submodule init +fi +git submodule update +cd validate/ + +# source helper functions +if test ! -f common/gst-autogen.sh; +then + echo There is something wrong with your source tree. + echo You are missing common/gst-autogen.sh + exit 1 +fi +. common/gst-autogen.sh + +# install pre-commit hook for doing clean commits +if test ! \( -x .git/hooks/pre-commit -a -L .git/hooks/pre-commit \); +then + rm -f ../.git/hooks/pre-commit + ln -s ../../validate/multi-pre-commit.hook ../.git/hooks/pre-commit +fi + +# GNU gettext automake support doesn't get along with git. +# https://bugzilla.gnome.org/show_bug.cgi?id=661128 +if test -d po ; then + touch -t 200001010000 po/gst-validate-1.0.pot +fi + +CONFIGURE_DEF_OPT='--enable-maintainer-mode --enable-gtk-doc' + +if test "x$package" = "xgstreamer"; then + CONFIGURE_DEF_OPT="$CONFIGURE_DEF_OPT --enable-docbook --enable-failing-tests --enable-poisoning" +fi + +autogen_options $@ + +printf "+ check for build tools" +if test ! -z "$NOCHECK"; then echo ": skipped version checks"; else echo; fi +version_check "autoreconf" "autoreconf " \ + "ftp://ftp.gnu.org/pub/gnu/autoconf/" 2 68 || DIE=1 +version_check "pkg-config" "" \ + "http://www.freedesktop.org/software/pkgconfig" 0 8 0 || DIE=1 + +die_check $DIE + +# if no arguments specified then this will be printed +if test -z "$*" && test -z "$NOCONFIGURE"; then + echo "+ checking for autogen.sh options" + echo " This autogen script will automatically run ./configure as:" + echo " ./configure $CONFIGURE_DEF_OPT" + echo " To pass any additional options, please specify them on the $0" + echo " command line." +fi + +toplevel_check $srcfile + +# autopoint +if test -d po ; then + tool_run "autopoint" "--force" +fi + +# aclocal +if test -f acinclude.m4; then rm acinclude.m4; fi + +autoreconf --force --install || exit 1 + +test -n "$NOCONFIGURE" && { + echo "+ skipping configure stage for package $package, as requested." + echo "+ autogen.sh done." + exit 0 +} + +cd "$olddir" + +echo "+ running configure ... " +test ! -z "$CONFIGURE_DEF_OPT" && echo " default flags: $CONFIGURE_DEF_OPT" +test ! -z "$CONFIGURE_EXT_OPT" && echo " external flags: $CONFIGURE_EXT_OPT" +echo + +echo "$srcdir/configure" $CONFIGURE_DEF_OPT $CONFIGURE_EXT_OPT +"$srcdir/configure" $CONFIGURE_DEF_OPT $CONFIGURE_EXT_OPT || { + echo " configure failed" + exit 1 +} + +echo "Now type 'make' to compile $package." diff --git a/validate/common/ChangeLog b/validate/common/ChangeLog new file mode 100644 index 0000000..92d0d25 --- /dev/null +++ b/validate/common/ChangeLog @@ -0,0 +1,1712 @@ +2008-12-17 Edward Hervey + + * gst.supp: + And yet another variation of the GstAudioFilter leak. + +2008-12-15 Sebastian Dröge + + Patch by: Roland Illig + + * m4/gst-parser.m4: + Fix AG_GST_BISON_CHECK to handle version numbers with more than + two components (i.e. 2.4.1). Fixes bug #564507. + +2008-12-14 Edward Hervey + + * gst.supp: + And yet another variant of the GstAudioFilter leak. + +2008-12-13 Edward Hervey + + * gst.supp: + Added variants of leaks of dynamic pad templates created in + GstAudioFilter. + Add conditional jump triggered by getaddrinfo (maybe glibc-2.9). + +2008-12-12 Edward Hervey + + * gst.supp: + Fix leak in GIO called by gnomevfs. Nothing we can do about this. + +2008-12-12 Edward Hervey + + * gst.supp: + Added another suppression for dynamic pad templates, in this case + GstAudioFilter. + Added suppression for PangoLanguage which can never be freed + according to the Pango API. + +2008-12-12 Edward Hervey + + * gst.supp: + A whole bunch of suppressions detected on latest gentoo ~amd64. + Make some existing suppressions more generic (for subtle dependecy + code changes). + Added suppressions for glibc-2.9. + Added suppressions for new variants of ALSA leaks. + Added suppressions for a series of leaks in plugins registrations due + to some pad templates' caps calculated at runtime. + Added suppressions for variants of some leaks in pango/fontconfig. + Added suppressions for leak in gstffmpegcsp.c (nothing we can do + about it, but will only exist once). + +2008-12-04 Sebastian Dröge + + * m4/gst-plugin-docs.m4: + Remove the check if $have_gtk_doc equals yes as it's not defined + and $enable_gtk_doc should be good enough. + Also this restores the build of the plugin documentation. + +2008-12-01 Mark Nauwelaerts + + * gst.supp: + Add suppression variant for Ubuntu Hardy x86/64bit. + +2008-12-01 Stefan Kost + + * gtk-doc-plugins.mak: + * gtk-doc.mak: + Simplily uninstall rule. Its closer to upstream and fixes #150331. + +2008-11-29 Sebastian Dröge + + * m4/glib-gettext.m4: + Update glib-gettext.m4 from latest stable GLib release. + +2008-11-29 Sebastian Dröge + + Patch by: Cygwin Ports maintainer + + + * gettext.patch: + Update the gettext patch for use with gettext 0.17 which is + required to build with libtool 2.2 because of conflicts. + First part of bug #556091. + +2008-11-29 Sebastian Dröge + + * m4/gtk-doc.m4: + * m4/pkg.m4: + Update gtk-doc and pkg-config m4 macros from their latest releases. + +2008-11-20 Michael Smith + + * m4/as-objc.m4: + Fix objective C test macro when none of the compilers are found at all. + +2008-10-30 Stefan Kost + + * gtk-doc.mak: + Also cp the entities here to all xinlcude based docs (workaround for + not being able to set up a search path). + +2008-10-17 Jan Schmidt + + * gtk-doc.mak: + Don't clobber the real registry cache file when + building docs. + +2008-10-07 Jan Schmidt - Sun Microsystems + + * m4/gst-error.m4: + Also disable the bogus "loop not entered at top" warnings appearing on Sparc Forte builds. + +2008-10-06 Stefan Kost + + * gtk-doc.mak: + Apply the same fix as below to gtk-doc.mak. Somehow did not end up in + CVS. + +2008-09-05 David Schleef + + * gtk-doc-plugins.mak: Fix the check for gtkdoc-rebase: don't + pass the 'which' error back to make. This fix is more specific + than what is in upstream. + +2008-09-05 David Schleef + + * gtk-doc.mak: Fix the check for gtkdoc-rebase: don't pass the + 'which' error back to make. This fix is more specific than + what is in upstream. + +2008-09-04 Stefan Kost + + * gtk-doc-plugins.mak: + * gtk-doc.mak: + Get closer to upstream makefiles. Don't install index.sgml twice. Call + gtkdoc-rebase (if exists). + +2008-08-21 Stefan Kost + + * gtk-doc-plugins.mak: + Revert $(top_builddir) -> $(builddir) change of rev. 1.39 as there is + no variable called builddir. + +2008-07-31 Mark Nauwelaerts + + * gst.supp: + Add suppressions for Ubunty Hardy x86/64bit, similar to earlier + versions and 32bit variant. + +2008-07-31 Sebastian Dröge + + * m4/gst-feature.m4: + Remove GST_DISABLE_(ENUMTYPES|INDEX|URI). + +2008-07-21 Tim-Philipp Müller + + * m4/gst-error.m4:: + When checking for GST_ERROR_CXXFLAGS, check each compiler flag + individually, not all together. + +2008-07-20 Tim-Philipp Müller + + * m4/gst-parser.m4:: + Fix bison version number detection for older --version + output format (as bison 1.28 on OSX 10.4 outputs). + Fixes #543853. + +2008-07-12 Stefan Kost + + * plugins.xsl: + Split refsect2 also here to make "Element Pads" subtitle visible. + +2008-07-08 Sebastian Dröge + + * m4/gst-error.m4: + Add compiler flags to warn if declarations after statements or + variable length arrays are used. These are C99/GCC extensions and + are not supported by some compilers we want to support. + +2008-07-02 Mark Nauwelaerts + + * gtk-doc-plugins.mak: + Only clean doc maintainer stamps in maintainer-clean. Fixes #539977. + +2008-06-20 Sebastian Dröge + + * gstdoc-scangobj: + Always use format strings for printf-like functions, even if they just + print a string. Fixes bug #536981. + +2008-06-20 Sebastian Dröge + + * gtk-doc-plugins.mak: + * gtk-doc.mak: + Include CFLAGS and LDFLAGS in GTKDOC_CFLAGS and GTKDOC_LDFLAGS, + otherwise the values passed to configure are ignored. + Fixes bug #536978. + +2008-06-05 Tim-Philipp Müller + + * m4/gst-error.m4: + Add -fno-strict-aliasing when compiling with -Werror, to work around + warnings caused by G_LOCK with recent GLib versions (2.16.x) (#316221). + +2008-06-05 Jan Schmidt + + * gtk-doc.mak: + Don't copy html/*.png files unless they don't already exist + in the destdir. Fixes distcheck failure caused by permissions + problems trying to copy a file into the destdir when it already + exists. + +2008-05-28 Stefan Kost + + * plugins.xsl: + The class was not shown in plugin docs. Fix typo in changelog below. + +2008-05-22 Jan Schmidt + + * gstdoc-scangobj: + Emit warnings if one of the GTypes we're expecting is 0 + when scanning. + +2008-05-21 Felipe Contreras + + * gtk-doc-plugins.mak: + * gtk-doc.mak: + Fix installing png images when gtk-doc is disabled. + +2008-05-21 Felipe Contreras + + * gtk-doc-plugins.mak: + * gtk-doc.mak: + Fix make clean when gtk-doc is disabled and other cleanups. + +2008-05-17 Jan Schmidt + + * gtk-doc-plugins.mak: + Be more quiet when the files don't yet exist. + +2008-05-16 Jan Schmidt + + * gstdoc-scangobj: + Add a mechanism for adding 'implicitly created' GTypes into the + scan, allowing for documenting plugin-private base classes that + provide signals or properties for public elements. + + * gtk-doc-plugins.mak: + Use $(builddir) instead of $(top_builddir) in a few places - there's + no need to hard code 'docs/plugins' as the only useable path. + +2008-05-14 Peter Kjellerstedt + + * m4/gst-feature.m4: + Report plug-ins without external dependencies that will not be built + even when the name of the plug-in is a substring of another plug-in, + e.g., goom vs. goom2k1. + +2008-05-14 Tim-Philipp Müller + + * gst.supp: + Add suppression for glibc bug on gutsy/x86-64 + +2008-05-12 Stefan Kost + + * plugins.xsl: + Improve the layout of the caps, but splitting them on ";". + +2008-05-09 Sebastian Dröge + + Patch by: Brian Cameron + + * m4/gst-default.m4: + Don't set the default audio sink to the default visualizer. + Fixes bug #532295. + +2008-05-07 Tim-Philipp Müller + + * check.mak: (help): + Document GST_CHECKS environment variable in checks 'make help'. + +2008-05-06 Sebastian Dröge + + Patch by: Marc-Andre Lureau + + * scangobj-merge.py: + Don't depend on Twisted just for the OrderedDict but implement our + own ordered dictionary class. Fixes bug #531577. + +2008-04-23 Edward Hervey + + * gst.supp: + Re-arrange latest suppressions. + Add all known suppressions for ubuntu hardy. Same as for older + ubuntus, but with different codepaths. + +2008-04-22 Edward Hervey + + * gst.supp: Make tls leak suppression a bit more generic. + +2008-04-22 Edward Hervey + + * gst.supp: Fix ommission in latest commit. + Make tls leak suppression more generic in order to cover more + distributions (and hopefully also future distributions). + +2008-04-22 Edward Hervey + + * gst.supp: Add suppressions for Hardy. + They're just the newer versions of similar suppressions we had + for the previous versions of ubuntu. + +2008-04-15 Sebastian Dröge + + * Makefile.am: + * m4/Makefile.am: + Dist all files in common. Fixes bug #527984. + +2008-04-14 Tim-Philipp Müller + + * m4/gst-function.m4: + Rename AC_CACHE_VAL cache-ids to contain '_cv_' in order to make + autoconf-2.62 complain less. + +2008-04-13 Tim-Philipp Müller + + * m4/gst-args.m4: + * m4/gst-valgrind.m4: + Bump valgrind requirement to 3.0 (which was released in August 2005). + Fixes #489269. Also, check for version >=REQ and not >REQ. + +2008-04-09 Tim-Philipp Müller + + * m4/gst-default.m4: + Add --with-default-{audiosink|audiosrc|videosink|videosrc|visualizer} + configure switches (#519417). + +2008-04-03 Tim-Philipp Müller + + * m4/gst-args.m4: + Add --disable-foo switch for dependency-less plugins (#525586). + +2008-04-01 Sebastian Dröge + + * m4/gst-parser.m4: + Unconditionally require flex 2.5.31 and bison 1.875. + +2008-03-23 Sebastian Dröge + + * m4/gst-arch.m4: + amd64/x86_64 allows unaligned memory access too. + +2008-03-21 Sebastian Dröge + + * m4/gst-dowhile.m4: + Add macro that checks if the compiler supports do {} while (0) + macros and define HAVE_DOWHILE_MACROS if it does. This is + needed by glib/gmacros.h to use something else than + if (1) else for G_STMT_START/END when compling C++, which + causes compiler warnings because of ambigious else with g++ 4.3. + +2008-03-21 Sebastian Dröge + + * m4/gst-plugin-docs.m4: + * mangle-tmpl.py: + Don't depend on PyXML and use only XML modules that are shipped + with python. Fixes bug #519635. + +2008-03-07 Edward Hervey + + * m4/gtk-doc.m4: (GTK_DOC_CHECK): + The previous commit to this file by Stefan Kost mentionned checking for + SED, but NOT checking for gtkdoc-check (wth is that doing there ??). + Therefore, removing the check for gtkdoc-check + +2008-03-03 David Schleef + + * m4/ax_create_stdint_h.m4: Oops, checked in the wrong copy of + this file. (Update from upstream) + +2008-03-03 David Schleef + + * m4/ax_create_stdint_h.m4: Update from upstream. Fixes a bug + compiling with MSVC. + +2008-03-03 Edward Hervey + + * m4/pkg.m4: + Allow override of pkg-config results, as proposed by configure --help. + This is in fact just a backport from upstream pkg.m4. + Fixes #518892 + +2008-03-03 Peter Kjellerstedt + + * ChangeLog: + Changelog surgery of my previous commit to add bugzilla reference. + * m4/gst-args.m4: + Add AG_GST_CHECK_PLUGIN and AG_GST_DISABLE_PLUGIN to make it easier + to include and exclude plug-ins without external references, i.e., + plug-ins listed in GST_PLUGINS_SELECTED. (#498222) + +2008-03-03 Sebastian Dröge + + * gst.supp: + Add another glibc suppression. + +2008-02-29 Peter Kjellerstedt + + * m4/gst-feature.m4: + Make the comment before defines generated via AG_GST_CHECK_FEATURE + look nicer. (#498222) + +2008-02-26 Jan Schmidt + + * m4/Makefile.am: + * m4/as-gcc-inline-assembly.m4: + Add Dave Schleef's GCC inline assembly detection macro + for using in gst-plugins-good in the goom 2k4 plugin. + +2008-02-25 Andy Wingo + + * gst-autogen.sh: Instead of only passing certain arguments to + configure, pass anything that we didn't handle. Much friendlier. + Fixes #34412. + +2008-02-23 Jan Schmidt + + * m4/gst-error.m4: + Store the detected compiler flags into ERROR_CFLAGS rather than + ERROR_CXXFLAGS, and use the macro that checks the C compiler, not + the C++ one. + +2008-02-23 Tim-Philipp Müller + + * m4/gst-error.m4: + Reflow checks for additional warning flags so they're not + nested, which fixes the result reporting in the configure + output. + +2008-02-22 Tim-Philipp Müller + + * m4/as-compiler-flag.m4: + Add AS_CXX_COMPILER_FLAG + + * m4/gst-error.m4: + Add AG_GST_SET_ERROR_CXXFLAGS (Forte bits need testing) + +2008-02-22 Tim-Philipp Müller + + * gtk-doc-plugins.mak: + Add 'check-inspected-versions' target; this helps identify + files that should have been removed or where the version + number should (ideally) be updated before a release + (which doesn't happen automatically if the releaser doesn't + build that plugin locally). Not adding at a distcheck hook + yet though, because it's not really that important and would + probably also be a problem on buildbots. + +2008-02-22 Sebastian Dröge + + * gst.supp: + Add even more glibc 2.7 suppressions. + +2008-02-22 Sebastian Dröge + + * gst.supp: + Add another suppression for GLib caching some values after + the first call. + +2008-02-12 Sebastian Dröge + + Patch by: + Tim Mooney + + * m4/gst-error.m4: + Use no%E_MACRO_REDEFINED on Solaris to prevent compiler warnings. + Fixes bug #515905. + +2008-02-11 Sebastian Dröge + + * gst.supp: + Add a few more glibc 2.7 suppressions to make the avisubtitle unit + test valgrind clean. Fixes bug #515703. + +2008-02-08 Stefan Kost + + * ChangeLog: + Changelog surgery for last commit. + +2008-02-08 Stefan Kost + + * m4/gtk-doc.m4: + Conditionally check for SED. Also sync a bit with upstream macro. + +2008-02-08 Stefan Kost + + * gtk-doc-plugins.mak: + * gtk-doc.mak: + Use '$(SED)' instead of 'sed'. Don't use -i for in-place as its gnu + only, move to a temp file instead. + +2008-02-06 Stefan Kost + + * gtk-doc-plugins.mak: + * gtk-doc.mak: + As our docs are versioned, we need to patch the index.sgml file to have + correct paths there, unless we also want to fork gtk-doc's xsl (which + we don't). This hopefully fixes xrefs between modules. + +2008-02-02 Sebastian Dröge + + * m4/gst-feature.m4: + Use printf instead of echo as "echo -e" isn't POSIX and doesn't work + with strict POSIX shells like tcsh or dash and also not every platform + has a /bin/echo that supports it. + +2008-01-24 Stefan Kost + + * ChangeLog: + ChangeLog surgery. + + * gstdoc-scangobj: + Sync the object scanner with gtk-doc fixes. Update args and hierarchy + files. + +2008-01-20 Sebastian Dröge + + * check.mak: + * coverage/lcov.mak: + * gtk-doc-plugins.mak: + * release.mak: + Use $(MAKE) instead of make to fix the build if GNU make is called + something else on the system. + + * m4/as-docbook.m4: + Fix path for docbook.xsl if we have no /etc/xml/catalog and add a + docbook-xsl search path for FreeBSD. + +2008-01-18 Sebastian Dröge + + * gst.supp: + Add a suppression for a glibc bug: + http://valgrind.org/docs/manual/faq.html#faq.exit_errors> + +2008-01-18 Sebastian Dröge + + * gst.supp: + Add some more glibc 2.7 suppressions and make the GLib suppressions + for the home/tmp/etc directory caching a bit more generic. + +2008-01-18 Sebastian Dröge + + * gst.supp: + Add some glibc 2.7 supressions as found on Debian/unstable. + +2008-01-14 Jan Schmidt + + * download-translations: + Apparently I have problems with leaving things commented out when + I edit shell scripts. + +2008-01-12 Jan Schmidt + + * download-translations: + Remove bash-isms + +2008-01-12 Jan Schmidt + + * check-exports: + Restore the cleanup rm of our tmp file which I didn't mean to leave + commented out. + +2008-01-12 Jan Schmidt + + * check-exports: + Fixes to make check-export work on both Solaris and Linux + + * m4/gst-error.m4: + Disable extra warning category (argument mismatch) as an error + on Forte, as it prevents the libcheck fail_if macros from compiling. + + * win32.mak: + Substitute the GStreamer version so things will keep working in 0.11 + +2008-01-11 Tim-Philipp Müller + + Patch by: Peter Kjellerstedt + + * m4/gst-glib2.m4: + * m4/gst-libxml2.m4: + Improve/fix output from configure if either glib-2.0 or + libxml2 are not installed (#498222). + +2008-01-09 Stefan Kost + + * coverage/lcov.mak: + Update coverage make-rules: use them conditionaly, use libtool mode + and use lcov to cleanup. + +2007-12-18 Sebastian Dröge + + * glib-gen.mak: + Also use #include "header" instead of #include
for the + headers that were used to generate the source files for the same + reason as below. + + Remove whitespace before #include. + +2007-12-18 Sebastian Dröge + + * glib-gen.mak: + Use #include "header" instead of #include
for the generated + enum C files as the file will always be in the same directory and + some compilers seem to be a bit strict about that unless . is added + to the include path. + + Include all headers that were used to generate the source files in + the C file as they're used there. + +2007-12-17 Tim-Philipp Müller + + * win32.mak: (win32), (win32defs), (win32crlf): + Make check for CR LF in Visual C++ 6.0 project files + work, based on patch by David Schleef (#496722, #393626). + +2007-12-17 Tim-Philipp Müller + + * Makefile.am: + Don't forget to dist the new win32.mak. + +2007-12-17 Tim-Philipp Müller + + * win32.mak: (win32), (win32defs): + Move common win32 Makefile foo into this new file. + +2007-12-15 Stefan Kost + + * gtk-doc-plugins.mak: + * gtk-doc.mak: + We should have never forked this that much :/. + +2007-12-13 Tim-Philipp Müller + + * check-exports: + Fix build on the ppc64 build bot. + +2007-12-13 Tim-Philipp Müller + + * check-exports: + Suppress more unintentional exports (too much hassle to rename them, + since the win32 project files would need changing too). + +2007-12-12 Tim-Philipp Müller + + * Makefile.am: + check-exports should be disted. + +2007-12-12 Tim-Philipp Müller + + * check-exports: + Add quick'n'dirty script to check the exported symbols of a library + against the symbols in the corresponding .def file (#493983). Based + on script by Ole André Vadla Ravnås. + +2007-11-06 Jan Schmidt + + * gtk-doc-plugins.mak: + Fix distcheck by making sure the types files are treated like the + other gtkdoc-scangobj generated files. + +2007-09-21 Sebastian Dröge + + * m4/gst-args.m4: + Let the AG_GST_ARG_ENABLE_EXPERIMENTAL macro default to disable + building of experimental plugins. Nobody uses it yet and the + --enable--experimental stuff from gst-plugins-good defaults to + disable too. + +2007-09-06 Tim-Philipp Müller + + * gtk-doc-plugins.mak: + Just use the normal 'check' target and avoid a circular + dependency. + +2007-09-06 Tim-Philipp Müller + + * gtk-doc-plugins.mak: + Add rule to error out if .hierarchy file contains tabs. + +2007-08-20 Tim-Philipp Müller + + * download-translations: + * po.mak: + If there are new languages, they need to be added to po/LINGUAS. + +2007-08-20 Tim-Philipp Müller + + * download-translations: + * po.mak: + Fix up 'download-po' a bit, so that we find new translations + for languages that aren't in our po/LINGUAS file yet too. + +2007-07-16 Jan Schmidt + + * gst.supp: + Add a suppression for GLib caching the tmp dir seen on an + Ubuntu Feisty system. + +2007-07-13 Jan Schmidt + + * m4/gst-feature.m4: + If we want to use 'echo -e', call /bin/echo instead of the shell's + since -e is a bash extension, and our /bin/sh might not be being + provided by bash. + +2007-07-01 Thomas Vander Stichele + + * po.mak: + Translation project has moved. Also, no idea how this used to + work given that we weren't downloading a .po file. + +2007-06-25 Stefan Kost + + * gst-xmlinspect.py: + * plugins.xsl: + Also extract element caps for plugin-docs. Fixes parts of #117692. + +2007-06-21 Tim-Philipp Müller + + Patch by: Andreas Schwab + + * m4/gst-feature.m4: + Fix quoting (#449493). + +2007-06-10 Sebastian Dröge + + * m4/gst-parser.m4: + Only generate the parser if bison >= 1.875 _and_ flex >= 2.5.31 is + installed and use pre-generated sources otherwise. Fixes bug #444820. + +2007-05-11 Michael Smith + + * gst.supp: + Suppression variant for our good friend the TLS leak, this time for + Ubuntu Feisty/x86. + +2007-05-09 Tim-Philipp Müller + + * gtk-doc-plugins.mak: + Fix make distcheck again; change some spaces to tabs in makefile. + +2007-04-29 Thomas Vander Stichele + + * gtk-doc-plugins.mak (-module): + Error out when the html build step gives warnings, so they get + fixed properly. + +2007-04-23 Stefan Kost + + * m4/gst-feature.m4: + Add macro AG_GST_PARSE_SUBSYSTEM_DISABLES that checks the defines in + the configuration header and AC_DEFINES the setings. + +2007-04-19 Sebastian Dröge + + Patch by: Vincent Torri + + * m4/gst-parser.m4: + Put the AC_MSG_RESULT output in brackets to get it properly written to + the terminal. + +2007-04-18 Sebastian Dröge + + * m4/gst-parser.m4: + Check for flex >= 2.5.31 and set GENERATE_PARSER if we have at least + that version. Otherwise use pre-generated parser sources as we can't + raise the required flex version. HAVE_MT_SAVE_FLEX is obsolete now + as we use a new enough flex version anyway. First part of #349180 + +2007-04-10 Thomas Vander Stichele + + * m4/gst-check.m4: + Allow pre-setting the GST(PB)_TOOLS/PLUGINS_DIR variables to help + builds against older GStreamer. + +2007-03-25 Sebastian Dröge + + * m4/gst-parser.m4: + Fix the flex version check. It ignored the micro version before. + +2007-03-09 Jan Schmidt + + * check.mak: + Use the same timeout when generating valgrind suppressions as + running the valgrind test. + + * gst.supp: + Add some more suppressions and stuff. + +2007-03-08 Jan Schmidt + + * check.mak: + Make sure GSlice is disabled when building suppressions too. + + * gst.supp: + Add around *850* lines of suppressions for one-time initialisations + inside libasound and gconf/bonobo/ORBit. I feel so dirty. + +2007-03-07 Jan Schmidt + + * gst.supp: + add a suppression for this GConf flup on the FC5 buildbot. + +2007-03-06 Jan Schmidt + + * gst.supp: + Make the suppression a little more generic, to catch the FC5 + backtrace too. + +2007-03-06 Jan Schmidt + + * gst.supp: + Add a suppression for libcdio 0.76. It leaks an internal struct + when the CD-ROM device is not accessible. + +2007-02-28 Thomas Vander Stichele + + * m4/gst-arch.m4: + Move a line that was in the wrong macro + +2007-02-28 Thomas Vander Stichele + + * m4/gst.m4: + Add + * m4/gst-arch.m4: + * m4/gst-args.m4: + * m4/gst-check.m4: + * m4/gst-debuginfo.m4: + * m4/gst-default.m4: + * m4/gst-doc.m4: + * m4/gst-error.m4: + * m4/gst-feature.m4: + * m4/gst-function.m4: + * m4/gst-gettext.m4: + * m4/gst-glib2.m4: + * m4/gst-libxml2.m4: + * m4/gst-parser.m4: + * m4/gst-plugin-docs.m4: + * m4/gst-plugindir.m4: + * m4/gst-valgrind.m4: + * m4/gst-x11.m4: + Convert all macros to use AG_GST style so we can properly warn + when they're missing if configure.ac calls AG_GST_INIT + Will require update in all GStreamer modules. + +2007-02-11 Stefan Kost + + * m4/gst-args.m4: + Remove 'enable' from configure switch description as this leads to + confusing lines like "disable enable builing ...". + * m4/gst-feature.m4: + Fix comment to sound less horrible. + +2007-02-07 Tim-Philipp Müller + + Patch by: Will Newton + + * m4/gst-check.m4: + Use $PKG_CONFIG rather than pkg-config directly, the one in our path + might not be the one we want, like when cross-compiling. Also, other + macros such as PKG_CHECK_MODULES use $PKG_CONFIG, so we should + probably too just for consistency. Fixes #405288. + +2007-01-08 Tim-Philipp Müller + + * m4/gst-parser.m4: + Need to use double square brackets again so m4 doesn't remove them + (fixes #378931). + + * m4/gst-args.m4: + Use double square brackets here as well, for the same reason. + +2007-01-05 Tim-Philipp Müller + + * m4/gst-parser.m4: + Use 'sed' rather than 'tr' to strip trailing letters from version + numbers, since 'tr' might not be available and we know sed is + (#378931). + +2006-10-21 Tim-Philipp Müller + + * check.mak: + Increase default timeout under valgrind, 60 is just too short and + some tests take a bit longer these days and not everyone has a + beefy machine. + +2006-09-29 Michael Smith + + * gst.supp: + More suppressions for edgy. + +2006-09-28 Jan Schmidt + + * m4/gst-glib2.m4: + Use gmodule-no-export-2.0.pc instead of gmodule-2.0.pc - we neither + want nor need --export-dynamic (which ends up making us export a bunch + of unneeded symbols) + +2006-09-14 Tim-Philipp Müller + + * gst.supp: + Some suppressions for the more recent ld.so in ubuntu edgy. + +2006-08-23 Tim-Philipp Müller + + * gst.supp: + Shorten function trail so the suppression works on + my ubuntu dapper system with core cvs as well. + +2006-07-28 Jan Schmidt + + * gst.supp: + Extra suppressions from my Ubuntu x86_64 machine + +2006-07-24 Tim-Philipp Müller + + Patch by: Frederic Peters + + * m4/gst-parser.m4: + Need to double square brackets in .m4 files. Should fix bison + version detection with version numbers like 1.23a (#348354). + +2006-07-24 Jan Schmidt + + * check.mak: + Valgrind fails to find tests written in tests/check/ directly (rather + than a subdir) - because valgrind gets run with a filename that + doesn't contain a relative path, it goes searching /usr/bin instead. + Run with ./.... to make things work either way. + + * gtk-doc-plugins.mak: + Add $(top_builddir)/src as a place to look for plugins + when building too, since that's where gst-template keeps things + +2006-07-23 Stefan Kost + + Patch by: Frederic Peters + + * m4/gst-parser.m4: + Fix bison detection (#348354) + +2006-07-21 Stefan Kost + + * m4/gst-parser.m4: + check for bison and flex + +2006-07-13 Thomas Vander Stichele + + * m4/gst-plugin-docs.m4: + remove the configure argument for enabling plugin doc build; + having gtk-doc enabled and pyxml present is enough of a trigger + +2006-07-03 Thomas Vander Stichele + + * coverage/lcov.mak: + fix up rules to work with gst-python as well + run "make lcov" to test and generate the reports + run "make lcov-reset" to redo it after that + +2006-07-02 Thomas Vander Stichele + + * Makefile.am: + * check.mak: + add an inspect target that inspects every element feature, + so we can have that added for coverage + * coverage/lcov.mak: + add support for lcov + +2006-07-02 Thomas Vander Stichele + + * m4/gst-args.m4: + when building with gcov, reset CFLAGS and friends to O0 + +2006-07-02 Thomas Vander Stichele + + * m4/gst-args.m4: + Find the gcov that matches the gcc version + Only allow gcov if we use gcc + +2006-07-02 Thomas Vander Stichele + + * Makefile.am: + * coverage/coverage-report-entry.pl: + * coverage/coverage-report.pl: + * coverage/coverage-report.xsl: + copy coverage reporting files from dbus + +2006-07-01 Thomas Vander Stichele + + * m4/gst-args.m4: + libtool strips gcov's -f flags, so libgcov does not get + linked in. Setting GCOV_LIBS with -lgcov fixes libtool's + stripping + also show what pkg-config-path we set + +2006-06-22 Tim-Philipp Müller + + Patch by: Peter Kjellerstedt + + * m4/gst-feature.m4: + Show list of plugins without external dependencies that + will not be built as well (#344136). + +2006-06-15 Tim-Philipp Müller + + * m4/gst-plugin-docs.m4: + add GST_PLUGIN_DOCS, which checks for everything needed + to build the plugin docs (namely gtk-doc and pyxml); also + adds a new --enable-plugin-docs configure switch; will + set ENABLE_PLUGIN_DOCS conditional for use in Makefile.am + files (see #344039). + +2006-06-11 Thomas Vander Stichele + + * m4/gst-check.m4: + add GST_PKG_CHECK_MODULES, which in the normal case of checking + for a dependency lib for a plug-in only needs two arguments + to do the right thing. + * m4/gst-feature.m4: + clean up output a little of feature checking; also deal with + non-plug-in feature checks + * m4/Makefile.am: + * m4/gst-gstreamer.m4: + remove this file; it's a useless check + +2006-06-06 Thomas Vander Stichele + + * m4/gst-arch.m4: + add PPC64 so we can have separate structure sizes for it + +2006-06-05 Edward Hervey + + * gtk-doc.mak: + Check for the proper .devhelp2 file to remove. + +2006-05-31 Thomas Vander Stichele + + * gtk-doc.mak: + allow a magic variable to suppress errors from docbuilding + +2006-05-30 Thomas Vander Stichele + + * gtk-doc.mak: + error out if gtkdoc-mktmpl finds unused declarations + +2006-05-28 Edward Hervey + + * gst.supp: + Reverting previous commit. That's good to know, Edward, but why ? + +2006-05-28 Edward Hervey + + * gst.supp: + Added suppresion for memleak in g_option_context_parse on fc5-64 + +2006-05-19 Thomas Vander Stichele + + * m4/gst-check.m4: + set GSTPB_PLUGINS_DIR just like GST_PLUGINS_DIR + +2006-05-18 Tim-Philipp Müller + + * check.mak: + Fix 'make help' in check directories, it should be + 'valgrind.gen-suppressions' not 'valgrind-gen-suppressions' + (not changing target to match help string on purpose to keep + scripts etc. functional). + +2006-05-18 Thomas Vander Stichele + + Patch by: Peter Kjellerstedt + + * m4/gst-arch.m4: + add support for CRIS and CRISv32. + +2006-05-17 Jan Schmidt + + * m4/gst-args.m4: + Fix the macros for command-line supplied package and origin names + so they don't end up being configure as "" (Fixes #341479) + +2006-05-14 Jan Schmidt + + * gtk-doc.mak: + Add uninstall rule to remove .devhelp2 files. + +2006-05-09 Edward Hervey + + * gst.supp: + Add suppression for GSlice version of + g_type_init calloc leak + +2006-04-05 Michael Smith + + * gst.supp: + Delete a bogus suppression for the registry code. + Generalise a suppression for a glib bug (see #337404) + +2006-04-04 Michael Smith + + * gst.supp: + Add a leak suppression: the existing glibc-doesn't-free-TLS one + wasn't triggering here. + +2006-04-04 Michael Smith + + * gst.supp: + Add some minimally-neccesary suppressions for my x86/dapper system. + +2006-04-01 Thomas Vander Stichele + + * plugins.xsl: + Do not display an origin link if origin does not start with http + See #323798 + +2006-04-01 Thomas Vander Stichele + + * m4/gst-args.m4: + * m4/gst-feature.m4: + add more macros + * m4/gst-x11.m4: + X11-related checks + +2006-04-01 Thomas Vander Stichele + + * m4/as-version.m4: + newer version + * m4/gst-args.m4: + * m4/gst-doc.m4: + update and add other macros to be shared across projects + +2006-03-24 Thomas Vander Stichele + + * gst.supp: + add a suppression for g_parse_debug_string + +2006-03-23 Stefan Kost + + * gstdoc-scangobj: + sync fully with gtkdoc-0.15 + +2006-03-23 Stefan Kost + + * gstdoc-scangobj: + * gtk-doc.mak: + sync a little with gtk-doc mainline + +2006-03-17 Wim Taymans + + * gst.supp: + add another clone suppression + change all glibc suppressions to match 2.3.* + +2006-03-09 Thomas Vander Stichele + + * m4/check.m4: + fix test so it actually works when the normal check is used + over debian's/ubuntu's + +2006-03-08 Jan Schmidt + + * check.mak: + Set G_SLICE=always-malloc when valgrinding tests + (closes #333272) + +2006-02-21 Jan Schmidt + + * m4/gst-glib2.m4: + Fix debug output when the GLib version prerequisite is not found + +2006-02-13 Andy Wingo + + * m4/check.m4: Hack around Debian/Ubuntu's broken installation of + the PIC version of check as libcheck_pic.a. Should work with + cross-compilation too. Grr. + +2006-02-06 Thomas Vander Stichele + + * m4/gst-default.m4: + switch to auto* sinks for defaults + +2006-02-02 Wim Taymans + + * check.mak: + add a .valgrind.gen-suppressions target to aid in generating + suppressions + * gst.supp: + add more repressions from my debian glibc as of today + +2006-02-02 Thomas Vander Stichele + + * gtk-doc-plugins.mak: + only add srcdir/gst if it exists + +2006-01-30 Thomas Vander Stichele + + * release.mak: + don't complain about disted enums in win32 + +2006-01-20 Thomas Vander Stichele + + * m4/gst-check.m4: + AC_SUBST CFLAGS and LIBS + do a non-command because something is stripping out our AC_SUBST + +2006-01-20 Thomas Vander Stichele + + * m4/gst-args.m4: + * m4/gst-valgrind.m4: + properly give a "no" result manually when providing a + not-found action to fix configure output + +2006-01-20 Thomas Vander Stichele + + * m4/pkg.m4: + update with a more recent version + +2006-01-07 Thomas Vander Stichele + + * gettext.patch: + make Makefile depend on LINGUAS, so rebuilds work when adding + a language + +2006-01-03 Michael Smith + + * check.mak: + Clarify error message from valgrind test runs. + +2005-12-16 Thomas Vander Stichele + + * m4/gst-arch.m4: + define HOST_CPU + +2005-11-29 Thomas Vander Stichele + + * check.mak: + add a valgrind-forever target for tests + +2005-11-28 Thomas Vander Stichele + + * check.mak: + when a "make test.check" run fails, make it rerun the test with + at least debug level 2 + +2005-11-14 Thomas Vander Stichele + + * m4/Makefile.am: + * m4/gst-check.m4: + fix check for base plugins + * m4/gst-default.m4: + add m4 to set default elements + +2005-10-18 Thomas Vander Stichele + + * m4/gst-check.m4: + check for tools correctly + +2005-10-18 Thomas Vander Stichele + + * gtk-doc.mak: + only enable breaking on new API when make distcheck passes, + not before + +2005-10-18 Thomas Vander Stichele + + * m4/gst-check.m4: + Resurrect Julien's dead body and wipe his mind clean + +2005-10-18 Thomas Vander Stichele + + * m4/gst-check.m4: + Kill Julien + +2005-10-17 Julien MOUTTE + + * m4/gst-check.m4: I know Thomas will kill me but this + ifelse statement seems incorrect as it is always setting + required to "yes". With this one it seems to work. Fixes + build of gst-plugins-base on my setup where gstreamer-check + is definitely not present/required. + +2005-10-18 Stefan Kost + + * gtk-doc.mak: + make build break on new api that has not been added to the + sections file + +2005-10-17 Thomas Vander Stichele + + * m4/gst-glib2.m4: + * m4/Makefile.am: + * m4/gst-check.m4: + add macro for easy checks for GStreamer libs + +2005-10-16 Thomas Vander Stichele + + * m4/gst-glib2.m4: + update, warn in error cases + +2005-10-16 Thomas Vander Stichele + + * m4/gst-error.m4: + add GST_SET_DEFAULT_LEVEL + +2005-10-16 Thomas Vander Stichele + + * m4/Makefile.am: + * m4/gst-gettext.m4: + remove the AM_GNU_GETTEXT* calls, they need to be in configure.ac + * m4/gst-glib2.m4: + clean up and re-use in core soon + * m4/gst-plugindir.m4: + macro to set up PLUGINDIR and plugindir define/var + +2005-10-15 Thomas Vander Stichele + + * m4/Makefile.am: + * m4/gst-gettext.m4: + add macro for setting up gettext + +2005-10-15 Thomas Vander Stichele + + * m4/gst-args.m4: + add some .m4's for argument checking that can be shared among modules + +2005-10-15 Thomas Vander Stichele + + * m4/as-libtool.m4: + set _LT_LDFLAGS + * m4/gst-libxml2.m4: + document + +2005-10-15 Thomas Vander Stichele + + * m4/gst-arch.m4: + indent a little + add AC_REQUIRE + * m4/gst-error.m4: + clean up + +2005-10-12 Thomas Vander Stichele + + * gst-autogen.sh: + update version detection expression to catch stuff like + Libtool (libtool15) 1.5.0 + +2005-10-11 Thomas Vander Stichele + + * gst.supp: + commit 6 new suppressions related to g_module_open; can these + really not be folded into one ? + +2005-10-11 Edward Hervey + + * gst.supp: + made the suppression more generic + Added pthread memleak suppresions + Added nss_parse_* memleak suppresion (used by g_option_context_parse) + +2005-10-11 Thomas Vander Stichele + + * check.mak: + be more strict, more leak resolution + * gst.supp: + clean up the g_type_init suppressions + +2005-10-07 Thomas Vander Stichele + + * m4/Makefile.am: + * m4/gst-valgrind.m4: + put the valgrind detection in an .m4 + +2005-09-29 Thomas Vander Stichele + + * check.mak: + add some more targets, like "help", but also more intensive tests + +2005-09-23 Thomas Vander Stichele + + * gtk-doc.mak: + make certain doc warnings fatal so people maintain docs again + +2005-09-23 Thomas Vander Stichele + + * Makefile.am: + * gtk-doc-plugins.mak: + * scangobj-merge.py: + merge additions from the .signals.new and .args.new file in + the original ones, only updating if necessary + +2005-09-23 Thomas Vander Stichele + + * gst-xmlinspect.py: + * gstdoc-scangobj: + * gtk-doc-plugins.mak: + fix properly for new API; make update in plugins dir now works + +2005-09-20 Thomas Vander Stichele + + * gst-xmlinspect.py: + * gstdoc-scangobj: + some fixes for new API + * gtk-doc-plugins.mak: + set environment properly + +2005-09-17 David Schleef + + * gtk-doc-plugins.mak: Use new environment variables. + +2005-09-16 Michael Smith + + * gstdoc-scangobj: + Make the scanobj code reflect registry/plugin API changes + +2005-09-15 Thomas Vander Stichele + + * gtk-doc-plugins.mak: + split out scanobj step (which will be run by doc maintainer) + from scan step (which will be run on every build) + clean up some of the commands for make distcheck + +2005-09-15 Thomas Vander Stichele + + * gtk-doc-plugins.mak: + * mangle-tmpl.py: + first stab at reorganizing the plugins build so we can maintain + element docs + +2005-09-14 David Schleef + + * as-libtool.mak: Remove + * m4/as-libtool.m4: The libtool bug that this worked around has + been fixed. + * m4/as-version.m4: Don't define GST_RELEASE, since it causes + config.h to be regenerated needlessly, and we don't use it. + +2005-09-14 Thomas Vander Stichele + + * gtk-doc-plugins.mak: + error out on inspect failure + +2005-09-14 Michael Smith + + * glib-gen.mak: + Don't call glib-mkenums with arguments that confuse/break MinGW, + fixes 316155. + +2005-09-03 Thomas Vander Stichele + + * gtk-doc-plugins.mak: + * gtk-doc.mak: + * m4/gst-doc.m4: + separate out gtk-doc and docbook stuff + have two separate --enable configure flags + +2005-08-26 Thomas Vander Stichele + + * check.mak: + add a .gdb target; rebuild registry for each target, otherwise + a code rebuild always triggers a reg rebuild, and it's just too + annoying + * gstdoc-scangobj: + +2005-08-21 Thomas Vander Stichele + + * check.mak: + separate out REGISTRY_ENVIRONMENT; we want to use that from + our valgrind runs, but we also want TESTS_ENVIRONMENT to contain + everything that the first test, gst-register, needs + +2005-08-21 Thomas Vander Stichele + + * check.mak: + parse output of valgrind and check for definitely lost, and error + out; somehow I was led to believe valgrind returns non-zero for + leaks, but I can't make it do that, so let's parse + +2005-08-20 Thomas Vander Stichele + + * check.mak: + for some weird reason valgrind does not report actual memleaks + if GST_PLUGIN_PATH is set to anything but the core gstreamer dir + while valgrind is running. Since the registry is going to go + anyway, I don't want to waste any more time on this; I just run + valgrind without GST_PLUGIN_PATH set. Since the registry loading + doesn't check if GST_PLUGIN_PATH got changed as a reason to rebuild + the registry, that's actually fine. + +2005-08-15 Thomas Vander Stichele + + * mangle-tmpl.py: + keep original Long_Description; only insert an include if it's + not already the first line in there + * plugins.xsl: + output more information for plugins, including an origin hyperlink + +2005-08-15 Thomas Vander Stichele + + * gst-xmlinspect.py: + a first stab at inspecting plugins and outputting an xml description + * gtk-doc-plugins.mak: + a gtk-doc using snippet for plugins documentation + * plugins.xsl: + a stylesheet to convert gst-xmlinspect.py output to docbook output + for inclusion in the gtk-doc stuff + +2005-07-20 Ronald S. Bultje + + * m4/gst-doc.m4: + s/pdf/eps/ in test for whether we output EPS images (#309379). + +2005-07-18 Andy Wingo + + * m4/as-libtool-tags.m4: Ooh, backported from libtool 1.6. Much + better. Thanks, Paolo Bonzini! + + * m4/Makefile.am (EXTRA_DIST): + * m4/as-libtool-tags.m4: New file, tries to disable some CXX and + fortran checks. + +2005-07-08 Thomas Vander Stichele + + * m4/gst-error.m4: + add macro to set ERROR_CFLAGS + +2005-06-30 Jan Schmidt + + * gst-autogen.sh: + Remove the old autoregen.sh if it exists before recreating it, + to prevent confusing any shell process that might be reading it + currently. + +2005-06-29 Thomas Vander Stichele + + * m4/gtk-doc.m4: + added + +2005-06-03 Stefan Kost + + * gst-autogen.sh: create autoregen.sh *before* shifting the options + +2005-05-17 Thomas Vander Stichele + + * gst-autogen.sh: only update autoregen.sh on actual runs + +2005-03-11 Thomas Vander Stichele + + * m4/check.m4: m4 from the check unit test suite + +2004-12-14 David Schleef + + * m4/gst-arch.m4: remove MMX stuff, since it doesn't work and + isn't needed anywhere + +2004-12-08 Thomas Vander Stichele + + * gst-autogen.sh: + allow failure command to be run so we can clean upfrom autopoint + +2004-09-03 Zeeshan Ali Khattak + * m4/gst-feature.m4: Trying to correct the GST_CHECK_CONFIGPROG macro + +2004-07-21 Benjamin Otte + + * m4/.cvsignore: exciting updates for libtool m4 files + +2004-07-12 David Schleef + + * m4/as-objc.m4: Add a macro to test for objective C + +2004-06-12 Thomas Vander Stichele + + * m4/gst-feature.m4: + not all of them support --plugin-libs, so redirect stderr + +2004-06-12 Thomas Vander Stichele + + * m4/as-scrub-include.m4: + sync with upstream to 0.1.4. Fixes #132440 + +2004-06-07 Benjamin Otte + + * m4/gst-feature.m4: + write a big marker into configure output when checking next plugin + to allow easier parsing of why plugins are(n't) built. + +2004-06-01 Thomas Vander Stichele + + * m4/as-compiler-flag.m4: + * m4/as-compiler.m4: + * m4/as-libtool.m4: + * m4/as-version.m4: + sync with upstream, change sticky options to -ko + +2004-05-24 Thomas Vander Stichele + + * m4/as-scrub-include.m4: synced with upstream + +2004-05-03 Thomas Vander Stichele + + * po.mak: + snippet for updating .po files + +2004-03-18 Thomas Vander Stichele + + * Makefile.am: + * m4/Makefile.am: + integrate these with the dist + +2004-03-17 Thomas Vander Stichele + + * release.mak: add a release target + +2004-03-09 Thomas Vander Stichele + + patch by: Stephane Loeuillet + + * m4/ax_create_stdint_h.m4: + use head -n instead of head - (#136500) + +2004-03-05 Thomas Vander Stichele + + * m4/gst-doc.m4: don't build PS without dvips binary + +2004-02-22 Julio M. Merino Vidal + + reviewed by: Benjamin Otte + + * m4/as-docbook.m4: + don't use == operator with test(1) (fixes #135115) + +2004-02-16 Thomas Vander Stichele + + * common/m4/gst-arch.m4: x86_64 is x86 too (clue from Fedora 2 test) + +2004-02-13 Thomas Vander Stichele + + * m4/gst-feature.m4: + remove AM_CONDITIONAL for the subsystem since automake 1.6.x + requires that call be in configure.ac + +2004-02-13 Thomas Vander Stichele + + * m4/gst-libxml2.m4: + take required version as argument, and default to 2.4.9 if not + specified + +2004-02-12 Thomas Vander Stichele + + * m4/gst-feature.m4: + rename and fix up GST_CHECK_DISABLE_SUBSYSTEM + +2004-02-11 Thomas Vander Stichele + + * common/m4/as-ac-expand.m4: + * common/m4/as-auto-alt.m4: + * common/m4/as-compiler-flag.m4: + * common/m4/as-compiler.m4: + * common/m4/as-docbook.m4: + * common/m4/as-libtool.m4: + * common/m4/as-scrub-include.m4: + * common/m4/as-version.m4: + * common/m4/glib-gettext.m4: + * common/m4/gst-arch.m4: + * common/m4/gst-debuginfo.m4: + * common/m4/gst-doc.m4: + * common/m4/gst-feature.m4: + * common/m4/gst-function.m4: + * common/m4/gst-glib2.m4: + * common/m4/gst-gstreamer.m4: + * common/m4/gst-libxml2.m4: + * common/m4/gst-makecontext.m4: + * common/m4/gst-mcsc.m4: + * common/m4/pkg.m4: + fix underquoted macros as reported by automake 1.8.x (#133800) + +2004-02-11 Johan Dahlin + + * gst-autogen.sh: Use A-Z instead of A-z in sed expression to + avoid a warning + +2004-02-05 Thomas Vander Stichele + + * m4/gst-doc.m4: + we use --output-format=xml and --ingnore-files options to + gtkdoc-mkdb, which got added between 0.9 and 1.0 + +2004-02-04 Thomas Vander Stichele + + * m4/as-libtool.m4: remove AM_PROG_LIBTOOL so it can move back + to configure.ac to shut up libtoolize + +2004-02-03 Thomas Vander Stichele + + * glib-gen.mak: added; used to generate enums and marshal code + +2004-01-13 Thomas Vander Stichele + + * gettext.patch: added; used by autogen.sh to make sure + GETTEXT_PACKAGE is understood from po/Makefile.in.in -> po/Makefile.in + diff --git a/validate/common/MAINTAINERS b/validate/common/MAINTAINERS new file mode 100644 index 0000000..72aac8e --- /dev/null +++ b/validate/common/MAINTAINERS @@ -0,0 +1,12 @@ +GStreamer is currently maintained by the consensus of a number +of people, including, but not limited to: + + Jan Schmidt + Wim Taymans + David Schleef + Tim-Philipp Müller + Sebastian Dröge + +Maintainer-related issues should be addressed to: + + gstreamer-devel@lists.freedesktop.org diff --git a/validate/common/Makefile.am b/validate/common/Makefile.am new file mode 100644 index 0000000..25966fc --- /dev/null +++ b/validate/common/Makefile.am @@ -0,0 +1,22 @@ +SUBDIRS = m4 + +EXTRA_DIST = \ + ChangeLog \ + gettext.patch \ + glib-gen.mak gtk-doc.mak upload-doc.mak \ + cruft.mak release.mak win32.mak po.mak \ + parallel-subdirs.mak \ + gst-autogen.sh \ + check-exports \ + c-to-xml.py mangle-tmpl.py scangobj-merge.py \ + gtk-doc-plugins.mak \ + plugins.xsl gstdoc-scangobj \ + gst.supp check.mak \ + coverage/lcov.mak \ + coverage/coverage-report.pl \ + coverage/coverage-report.xsl \ + coverage/coverage-report-entry.pl \ + download-translations \ + extract-release-date-from-doap-file \ + gst-indent \ + orc.mak diff --git a/validate/common/README b/validate/common/README new file mode 100644 index 0000000..83a1b88 --- /dev/null +++ b/validate/common/README @@ -0,0 +1,255 @@ +GStreamer @SERIES_VERSION@ + +WHAT IT IS +---------- + +This is GStreamer, a framework for streaming media. + +WHERE TO START +-------------- + +We have a website at +http://gstreamer.freedesktop.org/ + +You should start by going through our FAQ at +http://gstreamer.freedesktop.org/data/doc/gstreamer/head/faq/html/ + +There is more documentation; go to +http://gstreamer.freedesktop.org/documentation + +You can subscribe to our mailing lists; see the website for details. + +We track bugs in GNOME's bugzilla; see the website for details. + +You can join us on IRC - #gstreamer on irc.freenode.org + +GStreamer 1.0 series +-------------------- + +Starring + + GSTREAMER + +The core around which all other modules revolve. Base functionality and +libraries, some essential elements, documentation, and testing. + + BASE + +A well-groomed and well-maintained collection of GStreamer plug-ins and +elements, spanning the range of possible types of elements one would want +to write for GStreamer. + +And introducing, for the first time ever, on the development screen ... + + THE GOOD + + --- "Such ingratitude. After all the times I've saved your life." + +A collection of plug-ins you'd want to have right next to you on the +battlefield. Shooting sharp and making no mistakes, these plug-ins have it +all: good looks, good code, and good licensing. Documented and dressed up +in tests. If you're looking for a role model to base your own plug-in on, +here it is. + +If you find a plot hole or a badly lip-synced line of code in them, +let us know - it is a matter of honour for us to ensure Blondie doesn't look +like he's been walking 100 miles through the desert without water. + + THE UGLY + + --- "When you have to shoot, shoot. Don't talk." + +There are times when the world needs a color between black and white. +Quality code to match the good's, but two-timing, backstabbing and ready to +sell your freedom down the river. These plug-ins might have a patent noose +around their neck, or a lock-up license, or any other problem that makes you +think twice about shipping them. + +We don't call them ugly because we like them less. Does a mother love her +son less because he's not as pretty as the other ones ? No - she commends +him on his great personality. These plug-ins are the life of the party. +And we'll still step in and set them straight if you report any unacceptable +behaviour - because there are two kinds of people in the world, my friend: +those with a rope around their neck and the people who do the cutting. + + THE BAD + + --- "That an accusation?" + +No perfectly groomed moustache or any amount of fine clothing is going to +cover up the truth - these plug-ins are Bad with a capital B. +They look fine on the outside, and might even appear to get the job done, but +at the end of the day they're a black sheep. Without a golden-haired angel +to watch over them, they'll probably land in an unmarked grave at the final +showdown. + +Don't bug us about their quality - exercise your Free Software rights, +patch up the offender and send us the patch on the fastest steed you can +steal from the Confederates. Because you see, in this world, there's two +kinds of people, my friend: those with loaded guns and those who dig. +You dig. + +The Lowdown +----------- + + --- "I've never seen so many plug-ins wasted so badly." + +GStreamer Plug-ins has grown so big that it's hard to separate the wheat from +the chaff. Also, distributors have brought up issues about the legal status +of some of the plug-ins we ship. To remedy this, we've divided the previous +set of available plug-ins into four modules: + +- gst-plugins-base: a small and fixed set of plug-ins, covering a wide range + of possible types of elements; these are continuously kept up-to-date + with any core changes during the development series. + + - We believe distributors can safely ship these plug-ins. + - People writing elements should base their code on these elements. + - These elements come with examples, documentation, and regression tests. + +- gst-plugins-good: a set of plug-ins that we consider to have good quality + code, correct functionality, our preferred license (LGPL for the plug-in + code, LGPL or LGPL-compatible for the supporting library). + + - We believe distributors can safely ship these plug-ins. + - People writing elements should base their code on these elements. + +- gst-plugins-ugly: a set of plug-ins that have good quality and correct + functionality, but distributing them might pose problems. The license + on either the plug-ins or the supporting libraries might not be how we'd + like. The code might be widely known to present patent problems. + + - Distributors should check if they want/can ship these plug-ins. + - People writing elements should base their code on these elements. + +- gst-plugins-bad: a set of plug-ins that aren't up to par compared to the + rest. They might be close to being good quality, but they're missing + something - be it a good code review, some documentation, a set of tests, + a real live maintainer, or some actual wide use. + If the blanks are filled in they might be upgraded to become part of + either gst-plugins-good or gst-plugins-ugly, depending on the other factors. + + - If the plug-ins break, you can't complain - instead, you can fix the + problem and send us a patch, or bribe someone into fixing them for you. + - New contributors can start here for things to work on. + +PLATFORMS +--------- + +- Linux is of course fully supported +- FreeBSD is reported to work; other BSDs should work too +- Solaris is reported to work; a specific sunaudiosink plugin has been written +- MacOSX works, binary 1.x packages can be built using the cerbero build tool +- Windows works; binary 1.x packages can be built using the cerbero build tool + - MSys/MinGW builds + - Microsoft Visual Studio builds are not yet available or supported +- Android works, binary 1.x packages can be built using the cerbero build tool +- iOS works + +INSTALLING FROM PACKAGES +------------------------ + +You should always prefer installing from packages first. GStreamer is +well-maintained for a number of distributions, including Fedora, Debian, +Ubuntu, Mandrake, Gentoo, ... + +Only in cases where you: +- want to hack on GStreamer +- want to verify that a bug has been fixed +- do not have a sane distribution +should you choose to build from source tarballs or git. + +Find more information about the various packages at +http://gstreamer.freedesktop.org/download/ + +COMPILING FROM SOURCE TARBALLS +------------------------------ + +- again, make sure that you really need to install from source ! + If GStreamer is one of your first projects ever that you build from source, + consider taking on an easier project. + +- check output of ./configure --help to see if any options apply to you +- run + ./configure + make + + to build GStreamer. +- if you want to install it (not required, but what you usually want to do), run + make install + +- try out a simple test: + gst-launch -v fakesrc num_buffers=5 ! fakesink + (If you didn't install GStreamer, prefix gst-launch with tools/) + + If it outputs a bunch of messages from fakesrc and fakesink, everything is + ok. + + If it did not work, keep in mind that you might need to adjust the + PATH and/or LD_LIBRARY_PATH environment variables to make the system + find GStreamer in the prefix where you installed (by default that is /usr/local). + +- After this, you're ready to install gst-plugins, which will provide the + functionality you're probably looking for by now, so go on and read + that README. + +COMPILING FROM GIT +------------------ + +When building from git sources, you will need to run autogen.sh to generate +the build system files. + +You will need a set of additional tools typical for building from git, +including: +- autoconf +- automake +- libtool + +autogen.sh will check for recent enough versions and complain if you don't have +them. You can also specify specific versions of automake and autoconf with +--with-automake and --with-autoconf + +Check autogen.sh options by running autogen.sh --help + +autogen.sh can pass on arguments to configure + +When you have done this once, you can use autoregen.sh to re-autogen with +the last passed options as a handy shortcut. Use it. + +After the autogen.sh stage, you can follow the directions listed in +"COMPILING FROM SOURCE" + +You can also run your whole git stack uninstalled in your home directory, +so that you can quickly test changes without affecting your system setup or +interfering with GStreamer installed from packages. Many GStreamer developers +use an uninstalled setup for their work. + +There is a 'create-uninstalled-setup.sh' script in + + http://cgit.freedesktop.org/gstreamer/gstreamer/tree/scripts/ + +to easily create an uninstalled setup from scratch. + + +PLUG-IN DEPENDENCIES AND LICENSES +--------------------------------- + +GStreamer is developed under the terms of the LGPL (see LICENSE file for +details). Some of our plug-ins however rely on libraries which are available +under other licenses. This means that if you are distributing an application +which has a non-GPL compatible license (for instance a closed-source +application) with GStreamer, you have to make sure not to distribute GPL-linked +plug-ins. + +When using GPL-linked plug-ins, GStreamer is for all practical reasons +under the GPL itself. + +HISTORY +------- + +The fundamental design comes from the video pipeline at Oregon Graduate +Institute, as well as some ideas from DirectMedia. It's based on plug-ins that +will provide the various codec and other functionality. The interface +hopefully is generic enough for various companies (ahem, Apple) to release +binary codecs for Linux, until such time as they get a clue and release the +source. diff --git a/validate/common/README.static-linking b/validate/common/README.static-linking new file mode 100644 index 0000000..6d8299b --- /dev/null +++ b/validate/common/README.static-linking @@ -0,0 +1,174 @@ +================================= + GStreamer Static Linking README +================================= + +DRAFT, April 2013 + + + I. INTRODUCTION + +It is possible to link GStreamer libraries, plugins and applications +statically, both in case of free/libre/open-source software applications +and proprietary applications. On some platforms static linking may even +be required. + +However, distributing statically linked binaries using GStreamer usually +requires additional effort to stay compliant with the GNU LGPL v2.1 license. + +The purpose of this document is to draw attention to this fact, and to +summarise in layman's terms what we believe is required from anyone +distributing statically linked GStreamer binaries. Most of this also +applies to dynamically linked GStreamer binaries. + + + II. DISCLAIMER + +This document is not legal advice, nor is it comprehensive. It may use +words in ways that do not match the definition or use in the license +text. It may even be outright wrong. Read the license text for all the +details, it is the only legally binding document in this respect. + +This document is primarily concerned with the implications for the +distribution of binaries based on LGPL-licensed software as imposed by +the LGPL license, but there may be other restrictions to the distribution +of such binaries, such as terms and conditions of distribution channels +(e.g. "app stores"). + + + III. THE SPIRIT OF THE LGPL LICENSE + +The GNU LGPL v2.1 license allows use of such-licensed software by +proprietary applications, but still aims to ensure that at least the +LGPL-licensed software parts remain free under all circumstances. This +means any changes to LGPL-licensed source code must be documented and +be made available on request to those who received binaries of the +software. It also means that it must be possible to make changes to the +LGPL-licensed software parts and make the application use those, as far +as that is possible. And that recipients of an application using +LGPL-licensed software are made aware of their rights according to the +LGPL license. + +In an environment where GStreamer libraries and plugins are used as +dynamically-loaded shared objects (DLL/.so/.dyn files), this is usually +not a big problem, because it is fairly easy to compile a modified version +of the GStreamer libraries or LGPL plugins, and the application will/should +just pick up and use the modified version automatically. All that is needed +is for the original, LGPL-licensed source code and source code modifications +to be made available, and for a way to build the libraries or plugins for +the platform required (usually that will be using the build system scripts +that come with GStreamer, and using the typical build environment on the +system in question, but where that is not the case the needed build scripts +and/or tools would need to be provided as well). + + + IV. THINGS YOU NEED TO DO + + * You must tell users of your application that you are using LGPL-licensed + software, which LGPL-licensed software exactly, and you must provide them + with a copy of the license so they know their rights under the LGPL. + + * You must provide (on request) all the source code and all the changes + or additions you have made to the LGPL-licensed software you are using. + + For GStreamer code we would recommend that the changes be provided either + in form of a branch in a git repository, or as a set of "git format-patch"- + style patches against a GStreamer release or a snapshot of a GStreamer git + repository. The patches should ideally say what was changed and why it + was changed, and there should ideally be separate patches for independent + changes. + + * You must provide a way for users of your application to make changes to + the LGPL-licensed parts of the code, and re-create a full application + binary with the changes (using the standard toolchain and tools of the + target platform; if you are using a custom toolchain or custom tools + you must provide these and document how to use them to create a new + application binary). + + Note that this of course does not mean that the user is allowed to + re-distribute the changed application. Nor does it mean that you have + to provide your proprietary source code - it is sufficient to provide a + ready-made compiled object file that can be relinked into an application + binary with the re-compiled LGPL components. + + + V. THINGS TO LOOK OUT FOR + +While most GStreamer plugins and the libraries they depend on are licensed +under the LGPL or even more permissive licenses, that is not the case for +all plugins and libraries used, esp. those in the gst-plugins-ugly or +some of those in the gst-plugins-bad set of plugins. + +When statically linking proprietary code, care must be taken not to +statically link plugins or libraries that are licensed under less permissive +terms than the LGPL, such as e.g. GPL-licensed libraries. + + + VI. SPECIAL CONSIDERATIONS FOR SPECIFIC USE-CASES + + + 1. Proprietary GStreamer/GLib-based Application On iOS + +Let's assume an individual or a company wants to distribute a proprietary +iOS application that is built on top of GStreamer and GLib through +Apple's App Store. At the time of writing the Apple iPhone developer +agreement didn’t allow the bundling of shared libraries, so distributing +a proprietary iOS application with shared libraries is only possible using +distribution mechanisms outside of the App Store and/or only to jailbroken +devices, a prospect that may not appeal to our individual or company. So the +only alternative then is to link everything statically, which means the +obligations mentioned above come into play. + + + 2. Example: Jabber on iOS + +Tandberg (now Cisco) created a Jabber application for iOS, based on GStreamer. +On request they provided an LGPL compliance bundle in form of a zip file, with +roughly the following contents: + +buildapp.sh +readme.txt +Jabber/Jabber-Info.plist +Jabber/libip.a [236MB binary with proprietary code] +Jabber/main.mm +Jabber/xcconfig/Application.xcconfig +Jabber/xcconfig/Debug.xcconfig +Jabber/xcconfig/Release.xcconfig +Jabber/xcconfig/Shared.xcconfig +Jabber/Resources/*.lproj/Localizable.strings +Jabber/Resources/{Images,Audio,Sounds,IB,Message Styles,Emoticons,Fonts}/* +Jabber/Resources/* +Jabber.xcodeproj/project.pbxproj +Jabber.xcodeproj/project.xcworkspace/contents.xcworkspacedata +opensource/build/config.site +opensource/build/m4/movi.m4 +opensource/build/scripts/clean-deps.sh +opensource/build/scripts/fixup-makefile.sh +opensource/build/scripts/MoviMaker.py +opensource/build.sh +opensource/env.sh +opensource/Makefile +opensource/external/glib/* +opensource/external/gstreamer/{gstreamer,gst-plugins-*}/* +opensource/external/openssl/* +opensource/external/proxy-libintl/* +opensource/toolchain/darwin-x86/bin/{misc autotoools,m4,glib-mkenums,glib-genmarshal,libtool,pkg-config,etc.} +opensource/toolchain/darwin-x86/share/{aclocal,aclocal-1.11,autoconf,automake-1.11,libtool}/* +opensource/toolchain/darwin-x86/share/Config.pm +opensource/toolchain/darwin-x86/share/Config.pm.movi.in +patches/glib/glib.patch +patches/gst-plugins-bad/gst-plugins-bad.patch +patches/gst-plugins-base/gst-plugins-base.patch +patches/gst-plugins-good/gst-plugins-good.patch +patches/gstreamer/gstreamer.patch +patches/openssl/openssl.patch + +readme.txt starts with "This Readme file describes how to build the Cisco +Jabber for iPad application. You need to install Xcode, but the final package +is built by running buildapp.sh." and describes how to build project, +prerequisites, the procedure in detail, and a "How to Include Provisioning +Profile Manually / Alternate Code Signing Instructions" section. + + + 3. Random Links Which May Be Of Interest + +[0] http://multinc.com/2009/08/24/compatibility-between-the-iphone-app-store-and-the-lgpl/ diff --git a/validate/common/autogen.sh.in b/validate/common/autogen.sh.in new file mode 100755 index 0000000..87846a0 --- /dev/null +++ b/validate/common/autogen.sh.in @@ -0,0 +1,98 @@ +test -n "$srcdir" || srcdir=`dirname "$0"` +test -n "$srcdir" || srcdir=. + +olddir=`pwd` +cd "$srcdir" + +DIE=0 +package=@PACKAGE@ +srcfile=@SRCFILE@ + +# Make sure we have common +if test ! -f common/gst-autogen.sh; +then + echo "+ Setting up common submodule" + git submodule init +fi +git submodule update + +# source helper functions +if test ! -f common/gst-autogen.sh; +then + echo There is something wrong with your source tree. + echo You are missing common/gst-autogen.sh + exit 1 +fi +. common/gst-autogen.sh + +# install pre-commit hook for doing clean commits +if test ! \( -x .git/hooks/pre-commit -a -L .git/hooks/pre-commit \); +then + rm -f .git/hooks/pre-commit + ln -s ../../common/hooks/pre-commit.hook .git/hooks/pre-commit +fi + +# GNU gettext automake support doesn't get along with git. +# https://bugzilla.gnome.org/show_bug.cgi?id=661128 +if test -d po ; then + touch -t 200001010000 po/@PACKAGE@-@API_VERSION@.pot +fi + +CONFIGURE_DEF_OPT='--enable-maintainer-mode --enable-gtk-doc' + +if test "x$package" = "xgstreamer"; then + CONFIGURE_DEF_OPT="$CONFIGURE_DEF_OPT --enable-docbook --enable-failing-tests --enable-poisoning" +fi + +autogen_options $@ + +printf "+ check for build tools" +if test ! -z "$NOCHECK"; then echo ": skipped version checks"; else echo; fi +version_check "autoreconf" "autoreconf " \ + "ftp://ftp.gnu.org/pub/gnu/autoconf/" 2 68 || DIE=1 +version_check "pkg-config" "" \ + "http://www.freedesktop.org/software/pkgconfig" 0 8 0 || DIE=1 + +die_check $DIE + +# if no arguments specified then this will be printed +if test -z "$*" && test -z "$NOCONFIGURE"; then + echo "+ checking for autogen.sh options" + echo " This autogen script will automatically run ./configure as:" + echo " ./configure $CONFIGURE_DEF_OPT" + echo " To pass any additional options, please specify them on the $0" + echo " command line." +fi + +toplevel_check $srcfile + +# autopoint +if test -d po ; then + tool_run "autopoint" "--force" +fi + +# aclocal +if test -f acinclude.m4; then rm acinclude.m4; fi + +autoreconf --force --install || exit 1 + +test -n "$NOCONFIGURE" && { + echo "+ skipping configure stage for package $package, as requested." + echo "+ autogen.sh done." + exit 0 +} + +cd "$olddir" + +echo "+ running configure ... " +test ! -z "$CONFIGURE_DEF_OPT" && echo " default flags: $CONFIGURE_DEF_OPT" +test ! -z "$CONFIGURE_EXT_OPT" && echo " external flags: $CONFIGURE_EXT_OPT" +echo + +echo "$srcdir/configure" $CONFIGURE_DEF_OPT $CONFIGURE_EXT_OPT +"$srcdir/configure" $CONFIGURE_DEF_OPT $CONFIGURE_EXT_OPT || { + echo " configure failed" + exit 1 +} + +echo "Now type 'make' to compile $package." diff --git a/validate/common/c-to-xml.py b/validate/common/c-to-xml.py new file mode 100644 index 0000000..7a7a35b --- /dev/null +++ b/validate/common/c-to-xml.py @@ -0,0 +1,36 @@ +# -*- Mode: Python -*- +# vi:si:et:sw=4:sts=4:ts=4 + +""" +Convert a C program to valid XML to be included in docbook +""" + +from __future__ import print_function, unicode_literals + +import sys +import os +from xml.sax import saxutils + +def main(): + if len(sys.argv) == 1: + sys.stderr.write("Please specify a source file to convert") + sys.exit(1) + source = sys.argv[1] + + if not os.path.exists(source): + sys.stderr.write("%s does not exist.\n" % source) + sys.exit(1) + + content = open(source, "r").read() + + # print header + print ('') + print ('') + print () + print ('') + + # print content + print (saxutils.escape(content)) + print ('') + +main() diff --git a/validate/common/check-exports b/validate/common/check-exports new file mode 100755 index 0000000..ee01ff7 --- /dev/null +++ b/validate/common/check-exports @@ -0,0 +1,54 @@ +#!/bin/sh +# check-exports +# +# quick'n'dirty script that retrieves the list of exported symbols of a given +# library using 'nm', and compares that against the list of symbols-to-export +# of our win32/common/libfoo.def files. + +if [ $# -ne 2 ]; then + echo "Usage: $0 library.def library.so" + exit 1 +fi + +def_path="$1" +def_name="$(basename $def_path)" +lib_path="$2" + +lib_result="`mktemp /tmp/defname.XXXXXX`" + +LC_ALL=C +export LC_ALL + +# On Solaris, add -p to get the correct output format +NMARGS= +if nm -V 2>&1 |grep Solaris > /dev/null; then + NMARGS=-p +fi + +# _end is special cased because for some reason it is reported as an exported +# BSS symbol, unlike on linux where it's a local absolute symbol. +nm $NMARGS $lib_path | awk \ + '{ + if ($3 ~ /^[_]?(gst_|Gst|GST_).*/) + { + if ($2 ~ /^[BSDG]$/) + print "\t" $3 " DATA" + else if ($2 == "T") + print "\t" $3 + } + }' | sort | awk '{ if (NR == 1) print "EXPORTS"; print $0; }' \ + > $lib_result + +diffoutput=`diff -u $def_path $lib_result` +diffresult=$? + +rm $lib_result + +if test "$diffresult" -eq 0; then + exit 0; +else + echo -n "$diffoutput" >&2 + echo >&2 + exit 1; +fi + diff --git a/validate/common/check.mak b/validate/common/check.mak new file mode 100644 index 0000000..8a90b5d --- /dev/null +++ b/validate/common/check.mak @@ -0,0 +1,251 @@ +# keep target around, since it's referenced in the modules' Makefiles +clean-local-check: + @echo + +if HAVE_VALGRIND +# hangs spectacularly on some machines, so let's not do this by default yet +check-valgrind: + $(MAKE) valgrind +else +check-valgrind: + @true +endif + +LOOPS ?= 10 + +# run any given test by running make test.check +# if the test fails, run it again at at least debug level 2 +%.check: % + @$(AM_TESTS_ENVIRONMENT) \ + CK_DEFAULT_TIMEOUT=20 \ + $* || \ + $(AM_TESTS_ENVIRONMENT) \ + GST_DEBUG=$$GST_DEBUG,*:2 \ + CK_DEFAULT_TIMEOUT=20 \ + $* + +# just like 'check', but don't run it again if it fails (useful for debugging) +%.check-norepeat: % + @$(AM_TESTS_ENVIRONMENT) \ + CK_DEFAULT_TIMEOUT=20 \ + $* + +# run any given test in a loop +%.torture: % + @for i in `seq 1 $(LOOPS)`; do \ + $(AM_TESTS_ENVIRONMENT) \ + CK_DEFAULT_TIMEOUT=20 \ + $*; done + +# run any given test in an infinite loop +%.forever: % + @while true; do \ + $(AM_TESTS_ENVIRONMENT) \ + CK_DEFAULT_TIMEOUT=20 \ + $* || break; done + +# valgrind any given test by running make test.valgrind +%.valgrind: % + @valgrind_log=$(subst /,-,$*-valgrind.log); \ + $(AM_TESTS_ENVIRONMENT) \ + CK_DEFAULT_TIMEOUT=360 \ + G_SLICE=always-malloc \ + $(LIBTOOL) --mode=execute \ + $(VALGRIND_PATH) -q \ + $(foreach s,$(SUPPRESSIONS),--suppressions=$(s)) \ + --tool=memcheck --leak-check=full --trace-children=yes \ + --show-possibly-lost=no \ + --leak-resolution=high --num-callers=20 \ + ./$* 2>&1 | tee $$valgrind_log ; \ + if grep "==" $$valgrind_log > /dev/null 2>&1; then \ + rm $$valgrind_log; \ + exit 1; \ + fi ; \ + rm $$valgrind_log + +# valgrind any given test and generate suppressions for it +%.valgrind.gen-suppressions: % + @$(AM_TESTS_ENVIRONMENT) \ + CK_DEFAULT_TIMEOUT=360 \ + G_SLICE=always-malloc \ + $(LIBTOOL) --mode=execute \ + $(VALGRIND_PATH) -q \ + $(foreach s,$(SUPPRESSIONS),--suppressions=$(s)) \ + --tool=memcheck --leak-check=full --trace-children=yes \ + --show-possibly-lost=no \ + --leak-resolution=high --num-callers=20 \ + --gen-suppressions=all \ + ./$* 2>&1 | tee suppressions.log + +# valgrind torture any given test +%.valgrind-torture: % + @for i in `seq 1 $(LOOPS)`; do \ + $(MAKE) $*.valgrind || \ + (echo "Failure after $$i runs"; exit 1) || \ + exit 1; \ + done + @banner="All $(LOOPS) loops passed"; \ + dashes=`echo "$$banner" | sed s/./=/g`; \ + echo $$dashes; echo $$banner; echo $$dashes + +# valgrind any given test until failure by running make test.valgrind-forever +%.valgrind-forever: % + @while $(MAKE) $*.valgrind; do \ + true; done + +# gdb any given test by running make test.gdb +%.gdb: % + @$(AM_TESTS_ENVIRONMENT) \ + CK_FORK=no \ + $(LIBTOOL) --mode=execute \ + gdb $* + +%.lcov-reset: + $(MAKE) $*.lcov-run + $(MAKE) $*.lcov-report + +%.lcov: % + $(MAKE) $*.lcov-reset + +if GST_GCOV_ENABLED +%.lcov-clean: + $(MAKE) -C $(top_builddir) lcov-clean + +%.lcov-run: + $(MAKE) $*.lcov-clean + $(MAKE) $*.check + +%.lcov-report: + $(MAKE) -C $(top_builddir) lcov-report +else +%.lcov-run: + echo "Need to reconfigure with --enable-gcov" + +%.lcov-report: + echo "Need to reconfigure with --enable-gcov" +endif + +# torture tests +torture: $(TESTS) + -rm test-registry.* + @echo "Torturing tests ..." + @for i in `seq 1 $(LOOPS)`; do \ + $(MAKE) check || \ + (echo "Failure after $$i runs"; exit 1) || \ + exit 1; \ + done + @banner="All $(LOOPS) loops passed"; \ + dashes=`echo "$$banner" | sed s/./=/g`; \ + echo $$dashes; echo $$banner; echo $$dashes + +# forever tests +forever: $(TESTS) + -rm test-registry.* + @echo "Forever tests ..." + @while true; do \ + $(MAKE) check || \ + (echo "Failure"; exit 1) || \ + exit 1; \ + done + +# valgrind all tests +valgrind: $(TESTS) + @echo "Valgrinding tests ..." + @failed=0; valgrind_targets=""; \ + for t in $(filter-out $(VALGRIND_TESTS_DISABLE),$(TESTS)); do \ + valgrind_targets="$$valgrind_targets $$t.valgrind"; \ + done; \ + if ! $(MAKE) $$valgrind_targets ; then \ + echo "Some tests had leaks or errors under valgrind"; \ + false; \ + fi + +# valgrind all tests until failure +valgrind-forever: $(TESTS) + -rm test-registry.* + @echo "Forever valgrinding tests ..." + @while true; do \ + $(MAKE) valgrind || \ + (echo "Failure"; exit 1) || \ + exit 1; \ + done + +# valgrind torture all tests +valgrind-torture: $(TESTS) + -rm test-registry.* + @echo "Torturing and valgrinding tests ..." + @for i in `seq 1 $(LOOPS)`; do \ + $(MAKE) valgrind || \ + (echo "Failure after $$i runs"; exit 1) || \ + exit 1; \ + done + @banner="All $(LOOPS) loops passed"; \ + dashes=`echo "$$banner" | sed s/./=/g`; \ + echo $$dashes; echo $$banner; echo $$dashes + +# valgrind all tests and generate suppressions +valgrind.gen-suppressions: $(TESTS) + @echo "Valgrinding tests ..." + @failed=0; \ + for t in $(filter-out $(VALGRIND_TESTS_DISABLE),$(TESTS)); do \ + $(MAKE) $$t.valgrind.gen-suppressions; \ + if test "$$?" -ne 0; then \ + echo "Valgrind error for test $$t"; \ + failed=`expr $$failed + 1`; \ + whicht="$$whicht $$t"; \ + fi; \ + done; \ + if test "$$failed" -ne 0; then \ + echo "$$failed tests had leaks or errors under valgrind:"; \ + echo "$$whicht"; \ + false; \ + fi + +# inspect every plugin feature +GST_INSPECT = $(GST_TOOLS_DIR)/gst-inspect-$(GST_API_VERSION) +inspect: + @echo "Inspecting features ..." + @for e in `$(AM_TESTS_ENVIRONMENT) $(GST_INSPECT) | head -n -2 \ + | cut -d: -f2`; \ + do echo Inspecting $$e; \ + $(GST_INSPECT) $$e > /dev/null 2>&1; done + +# build all tests +build-checks: $(TESTS) + +help: + @echo + @echo "make check -- run all checks" + @echo "make torture -- run all checks $(LOOPS) times" + @echo "make (dir)/(test).check -- run the given check once, repeat with GST_DEBUG=*:2 if it fails" + @echo "make (dir)/(test).check-norepeat -- run the given check once, but don't run it again if it fails" + @echo "make (dir)/(test).forever -- run the given check forever" + @echo "make (dir)/(test).torture -- run the given check $(LOOPS) times" + @echo + @echo "make (dir)/(test).gdb -- start up gdb for the given test" + @echo + @echo "make valgrind -- valgrind all tests" + @echo "make valgrind-forever -- valgrind all tests forever" + @echo "make valgrind-torture -- valgrind all tests $(LOOPS) times" + @echo "make valgrind.gen-suppressions -- generate suppressions for all tests" + @echo " and save to suppressions.log" + @echo "make (dir)/(test).valgrind -- valgrind the given test" + @echo "make (dir)/(test).valgrind-forever -- valgrind the given test forever" + @echo "make (dir)/(test).valgrind-torture -- valgrind the given test $(LOOPS) times" + @echo "make (dir)/(test).valgrind.gen-suppressions -- generate suppressions" + @echo " and save to suppressions.log" + @echo "make inspect -- inspect all plugin features" + @echo "make build-checks -- build all checks (but don't run them)" + @echo + @echo + @echo "Additionally, you can use the GST_CHECKS environment variable to" + @echo "specify which test(s) should be run. This is useful if you are" + @echo "debugging a failure in one particular test, or want to reproduce" + @echo "a race condition in a single test." + @echo + @echo "Examples:" + @echo + @echo " GST_CHECKS=test_this,test_that make element/foobar.check" + @echo " GST_CHECKS=test_many_threads make element/foobar.forever" + @echo + diff --git a/validate/common/coverage/coverage-report-entry.pl b/validate/common/coverage/coverage-report-entry.pl new file mode 100644 index 0000000..d0036b3 --- /dev/null +++ b/validate/common/coverage/coverage-report-entry.pl @@ -0,0 +1,69 @@ +#!/usr/bin/perl +# +# Copyright (C) 2006 Daniel Berrange +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +print < + +Coverage report for $ARGV[0] + + + +

Coverage report for $ARGV[0]

+ +
+EOF
+
+
+while (<>) {
+    s/&/&/g;
+    s//>/g;
+
+    if (/^\s*function (\S+) called (\d+) returned \d+% blocks executed \d+%/) {
+	my $class = $2 > 0 ? "perfect" : "terrible";
+	$_ = "$_";
+    } elsif (/^\s*branch\s+\d+\s+taken\s+(\d+)%\s+.*$/) {
+	my $class = $1 > 0 ? "perfect" : "terrible";
+	$_ = "$_";
+    } elsif (/^\s*branch\s+\d+\s+never executed.*$/) {
+	my $class = "terrible";
+	$_ = "$_";
+    } elsif (/^\s*call\s+\d+\s+never executed.*$/) {
+	my $class = "terrible";
+	$_ = "$_";
+    } elsif (/^\s*call\s+\d+\s+returned\s+(\d+)%.*$/) {
+	my $class = $1 > 0 ? "perfect" : "terrible";
+	$_ = "$_";
+    }
+
+    print;
+}
+
+print <
+
+
+EOF
diff --git a/validate/common/coverage/coverage-report.pl b/validate/common/coverage/coverage-report.pl
new file mode 100644
index 0000000..18bd6f1
--- /dev/null
+++ b/validate/common/coverage/coverage-report.pl
@@ -0,0 +1,125 @@
+#!/usr/bin/perl
+#
+# Copyright (C) 2006 Daniel Berrange
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+use warnings;
+use strict;
+
+my %coverage = ( functions => {}, files => {} );
+
+my %filemap;
+
+my $type;
+my $name;
+
+my @functions;
+
+while (<>) {
+    if (/^Function '(.*)'\s*$/) {
+	$type = "function";
+	$name = $1;
+	$coverage{$type}->{$name} = {};
+	push @functions, $name;
+    } elsif (/^File '(.*?)'\s*$/) {
+	$type = "file";
+	$name = $1;
+	$coverage{$type}->{$name} = {};
+
+	foreach my $func (@functions) {
+	    $coverage{"function"}->{$func}->{file} = $name;
+	}
+	@functions = ();
+    } elsif (/^Lines executed:(.*)%\s*of\s*(\d+)\s*$/) {
+	$coverage{$type}->{$name}->{lines} = $2;
+	$coverage{$type}->{$name}->{linesCoverage} = $1;
+    } elsif (/^Branches executed:(.*)%\s*of\s*(\d+)\s*$/) {
+	$coverage{$type}->{$name}->{branches} = $2;
+	$coverage{$type}->{$name}->{branchesCoverage} = $1;
+    } elsif (/^Taken at least once:(.*)%\s*of\s*(\d+)\s*$/) {
+	$coverage{$type}->{$name}->{conds} = $2;
+	$coverage{$type}->{$name}->{condsCoverage} = $1;
+    } elsif (/^Calls executed:(.*)%\s*of\s*(\d+)\s*$/) {
+	$coverage{$type}->{$name}->{calls} = $2;
+	$coverage{$type}->{$name}->{callsCoverage} = $1;
+    } elsif (/^No branches$/) {
+	$coverage{$type}->{$name}->{branches} = 0;
+	$coverage{$type}->{$name}->{branchesCoverage} = "100.00";
+	$coverage{$type}->{$name}->{conds} = 0;
+	$coverage{$type}->{$name}->{condsCoverage} = "100.00";
+    } elsif (/^No calls$/) {
+	$coverage{$type}->{$name}->{calls} = 0;
+	$coverage{$type}->{$name}->{callsCoverage} = "100.00";
+    } elsif (/^\s*(.*):creating '(.*)'\s*$/) {
+	$filemap{$1} = $2;
+    } elsif (/^\s*$/) {
+	# nada
+    } else {
+	warn "Shit [$_]\n";
+    }
+}
+
+my %summary;
+foreach my $type ("function", "file") {
+    $summary{$type} = {};
+    foreach my $m ("lines", "branches", "conds", "calls") {
+	my $totalGot = 0;
+	my $totalMiss = 0;
+	my $count = 0;
+	foreach my $func (keys %{$coverage{function}}) {
+	    $count++;
+	    my $got = $coverage{function}->{$func}->{$m};
+	    $totalGot += $got;
+	    my $miss = $got * $coverage{function}->{$func}->{$m ."Coverage"} / 100;
+	    $totalMiss += $miss;
+	}
+	$summary{$type}->{$m} = sprintf("%d", $totalGot);
+	$summary{$type}->{$m . "Coverage"} = sprintf("%.2f", $totalMiss / $totalGot * 100);
+    }
+}
+
+
+
+print "\n";
+
+foreach my $type ("function", "file") {
+    printf "<%ss>\n", $type;
+    foreach my $name (sort { $a cmp $b } keys %{$coverage{$type}}) {
+	my $rec = $coverage{$type}->{$name};
+	printf "  \n", $name, ($type eq "file" ? $filemap{$name} : $filemap{$rec->{file}});
+	printf "    \n", $rec->{lines}, $rec->{linesCoverage};
+	if (exists $rec->{branches}) {
+	    printf "    \n", $rec->{branches}, $rec->{branchesCoverage};
+	}
+	if (exists $rec->{conds}) {
+	    printf "    \n", $rec->{conds}, $rec->{condsCoverage};
+	}
+	if (exists $rec->{calls}) {
+	    printf "    \n", $rec->{calls}, $rec->{callsCoverage};
+	}
+	print  "  \n";
+    }
+
+    printf "  \n";
+    printf "    \n", $summary{$type}->{lines}, $summary{$type}->{linesCoverage};
+    printf "    \n", $summary{$type}->{branches}, $summary{$type}->{branchesCoverage};
+    printf "    \n", $summary{$type}->{conds}, $summary{$type}->{condsCoverage};
+    printf "    \n", $summary{$type}->{calls}, $summary{$type}->{callsCoverage};
+    printf  "  \n";
+    printf "\n", $type;
+}
+
+print "\n";
diff --git a/validate/common/coverage/coverage-report.xsl b/validate/common/coverage/coverage-report.xsl
new file mode 100644
index 0000000..3fe124b
--- /dev/null
+++ b/validate/common/coverage/coverage-report.xsl
@@ -0,0 +1,235 @@
+
+
+
+
+  
+
+  
+    
+      
+        Coverage report
+        
+      
+      
+        

Coverage report

+ + + +
+ + +

Function coverage

+ + + +
+ + + +

File coverage

+ + + +
+ + + + + + + + + + + + + + + + + + + + + odd + + + even + + + + + + + + + + + + + + odd + + + even + + + + + + +
NameLinesBranchesConditionsCalls
+
+ + + + + + + + + + + + + + Summary + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + perfect + + + excellant + + + good + + + poor + + + bad + + + terrible + + + + + % of + + +
diff --git a/validate/common/coverage/lcov.mak b/validate/common/coverage/lcov.mak new file mode 100644 index 0000000..b45728a --- /dev/null +++ b/validate/common/coverage/lcov.mak @@ -0,0 +1,47 @@ +## .PHONY so it always rebuilds it +.PHONY: lcov-reset lcov lcov-run lcov-report lcov-upload lcov-clean + +# run lcov from scratch, always +lcov-reset: + $(MAKE) lcov-run + $(MAKE) lcov-report + +# run lcov from scratch if the dir is not there +lcov: + $(MAKE) lcov-reset + +if GST_GCOV_ENABLED +# reset lcov stats +lcov-clean: + @-rm -rf lcov + lcov --directory . --zerocounters + +# reset run coverage tests +lcov-run: + -$(MAKE) lcov-clean + -if test -d tests/check; then $(MAKE) -C tests/check inspect; fi + -$(MAKE) check + +# generate report based on current coverage data +lcov-report: + mkdir lcov + lcov --compat-libtool --directory . --capture --output-file lcov/lcov.info + lcov --list-full-path -l lcov/lcov.info | grep -v "`cd $(top_srcdir) && pwd`" | cut -d\| -f1 > lcov/remove + lcov --list-full-path -l lcov/lcov.info | grep "tests/check/" | cut -d\| -f1 >> lcov/remove + lcov --list-full-path -l lcov/lcov.info | grep "docs/plugins/" | cut -d\| -f1 >> lcov/remove + lcov -r lcov/lcov.info `cat lcov/remove` > lcov/lcov.cleaned.info + rm lcov/remove + mv lcov/lcov.cleaned.info lcov/lcov.info + genhtml -t "$(PACKAGE_STRING)" -o lcov --num-spaces 2 lcov/lcov.info + +lcov-upload: lcov + rsync -rvz -e ssh --delete lcov/* gstreamer.freedesktop.org:/srv/gstreamer.freedesktop.org/www/data/coverage/lcov/$(PACKAGE) + +else +lcov-run: + echo "Need to reconfigure with --enable-gcov" + +lcov-report: + echo "Need to reconfigure with --enable-gcov" +endif + diff --git a/validate/common/cruft.mak b/validate/common/cruft.mak new file mode 100644 index 0000000..53eec24 --- /dev/null +++ b/validate/common/cruft.mak @@ -0,0 +1,56 @@ +# checks for left-over files in the (usually uninstalled) tree, ie. for +# stuff that best be deleted to avoid problems like having old plugin binaries +# lying around. +# +# set CRUFT_FILES and/or CRUFT_DIRS in your Makefile.am when you include this + +check-cruft: + @cruft_files=""; cruft_dirs=""; \ + for f in $(CRUFT_FILES); do \ + if test -e $$f; then \ + cruft_files="$$cruft_files $$f"; \ + fi \ + done; \ + for d in $(CRUFT_DIRS); do \ + if test -e $$d; then \ + cruft_dirs="$$cruft_dirs $$d"; \ + fi \ + done; \ + if test "x$$cruft_files$$cruft_dirs" != x; then \ + echo; \ + echo "**** CRUFT ALERT *****"; \ + echo; \ + echo "The following files and directories may not be needed any "; \ + echo "longer (usually because a plugin has been merged into "; \ + echo "another plugin, moved to a different module, or been "; \ + echo "renamed), and you probably want to clean them up if you "; \ + echo "don't have local changes: "; \ + echo; \ + for f in $$cruft_files; do echo "file $$f"; done; \ + echo; \ + for d in $$cruft_dirs; do echo "directory $$d"; done; \ + echo; \ + echo "'make clean-cruft' will remove these for you."; \ + echo; \ + fi + +clean-cruft-dirs: + @for d in $(CRUFT_DIRS); do \ + if test -e $$d; then \ + rm -r "$$d" && echo "Removed directory $$d"; \ + fi \ + done + +clean-cruft-files: + @for f in $(CRUFT_FILES); do \ + if test -e $$f; then \ + rm "$$f" && echo "Removed file $$f"; \ + fi \ + done + +clean-cruft: clean-cruft-dirs clean-cruft-files + +# also might want to add this to your Makefile.am: +# +# all-local: check-cruft + diff --git a/validate/common/download-translations b/validate/common/download-translations new file mode 100755 index 0000000..aef1d31 --- /dev/null +++ b/validate/common/download-translations @@ -0,0 +1,152 @@ +#!/bin/sh +# Shell script to download the latest translations for a given GStreamer +# package from translationproject.org + + +# DOMAINS based on http://translationproject.org/extra/matrix.html +# We need to check all domains, not only po/LINGUAS, since there might be +# new translations +DOMAINS=\ +"af am ar az be bg pt_BR bs ca zh_CN cs cy da de el eo es et eu fa fi fr "\ +"ga en_GB gl gu he hi zh_HK hr hu id is it ja ko ku ky lg lt lv mk mn ms "\ +"mt nb ne nl nn or pa pl pt rm ro ru rw sk sl sq sr sv ta tq th tk "\ +"tr zh_TW uk ven vi wa xh zu" + +# for testing/debugging: +#DOMAINS="es fr hu sv pl xx" + +# check for 'diff' program +diff --version 2>/dev/null >/dev/null +if [ ! $? ]; then + echo "==== You must have the 'diff' program installed for this script ====" + exit 1 +fi + +# check for 'wget' program +wget --version 2>/dev/null >/dev/null +if [ ! $? ]; then + echo "==== You must have the 'wget' program installed for this script ====" + exit 1 +fi + +# make sure we're in the top-level directory +if [ ! -d ./po ]; then + echo "==== No ./po directory in the current working directory ====" + exit 1 +fi + +# make sure a package argument was passed to us +if [ -z "$1" ]; then + echo "Usage: $0 PACKAGE, e.g. $0 gst-plugins-good" + exit 1 +fi + +if test "$1" != "gstreamer" -a \ + "$1" != "gst-plugins-base" -a \ + "$1" != "gst-plugins-good" -a \ + "$1" != "gst-plugins-ugly" -a \ + "$1" != "gst-plugins-bad"; then + echo "Unexpected package '$1' ?!" + exit 1 +fi + +PACKAGE="$1" + +DOMAINS_TO_ADD="" +DOMAINS_UPDATED="" +DOMAINS_NOT_IN_LINGUAS="" + +echo "Downloading latest translation files for package $PACKAGE ..." +echo + +for d in $DOMAINS +do + PACKAGE_PO_URL_BASE="http://translationproject.org/latest/$PACKAGE" + PO_URL="$PACKAGE_PO_URL_BASE/$d.po" + PO_FILENAME="$PACKAGE.$d.po" + if wget -q -nc -O $PO_FILENAME $PO_URL; then + # we want all .po files in UTF-8 format really, so convert if needed.. + CHARSET=`grep Content-Type $PO_FILENAME | sed -e 's/.*charset=\(.*\)\\\\n.*/\1/'` + if test "x$CHARSET" != "xUTF-8" -a "x$CHARSET" != "xutf-8"; then + # note: things like the bugs address will be added back by make update-po + if msguniq $PO_FILENAME --no-location \ + --output-file=$PO_FILENAME.utf8 \ + --to-code=UTF-8; then + mv $PO_FILENAME.utf8 $PO_FILENAME + else + echo "**** $d: conversion from $CHARSET to UTF-8 failed ****" + fi + fi + if [ -f "po/$d.po" ]; then + # ./po/foo.po exists, so let's check if ours matches the latest from the + # translation project website + REVDATE_NEW=`grep PO-Revision-Date $PO_FILENAME`; + REVDATE_OLD=`grep PO-Revision-Date po/$d.po`; + CHARSET_OLD=`grep Content-Type po/$d.po | sed -e 's/.*charset=\(.*\)\\\\n.*/\1/'` + if test "x$REVDATE_NEW" = "x$REVDATE_OLD" -a "x$CHARSET_OLD" = "xUTF-8"; then + # note: source code line markers will be removed later by make upload-po + echo "$d.po: up-to-date" + rm -f $PO_FILENAME + else + mv $PO_FILENAME "po/$d.po" + if test "x$CHARSET_OLD" != "xUTF-8" -a "x$CHARSET_OLD" != "xutf-8"; then + echo "$d.po: update (and charset converted from $CHARSET_OLD to UTF-8)" + else + echo "$d.po: updated" + fi + DOMAINS_UPDATED="$DOMAINS_UPDATED $d" + fi + # make sure domain is listed in LINGUAS + if ! grep $d "po/LINGUAS" >/dev/null 2>/dev/null; then + DOMAINS_NOT_IN_LINGUAS="$DOMAINS_NOT_IN_LINGUAS $d" + fi + else + # ./po/foo.po doesn't exist, but foo.po exists on the translation project + # website, so it's probably a new translation + echo "$d.po: new language" + mv $PO_FILENAME "po/$d.po" + DOMAINS_UPDATED="$DOMAINS_UPDATED $d" + DOMAINS_TO_ADD="$DOMAINS_TO_ADD $d" + fi + else + rm -f $PO_FILENAME + echo "$d.po: failure (does probably not exist)" + fi +done + +if [ -n "$DOMAINS_UPDATED" ]; then + echo "====================================================================" + echo + echo "Language domains updated :$DOMAINS_UPDATED" + echo "Language domains to git add :$DOMAINS_TO_ADD" + echo + echo "Source: http://translationproject.org/latest/$PACKAGE/" + echo + if [ -n "$DOMAINS_TO_ADD" ]; then + CMD_STRING="git add" + for d in $DOMAINS_TO_ADD; do + CMD_STRING="$CMD_STRING po/$d.po" + done + echo "Please run" + echo + echo " $CMD_STRING" + echo + echo "now and add the following domains to the po/LINGUAS file:" + echo + echo " $DOMAINS_TO_ADD" + echo + echo + fi + echo "====================================================================" +fi + +if [ -n "$DOMAINS_NOT_IN_LINGUAS" ]; then + echo + echo "Existing domains missing from the po/LINGUAS file:" + echo + echo " $DOMAINS_NOT_IN_LINGUAS" + echo + echo +fi + + diff --git a/validate/common/extract-release-date-from-doap-file b/validate/common/extract-release-date-from-doap-file new file mode 100755 index 0000000..f57e307 --- /dev/null +++ b/validate/common/extract-release-date-from-doap-file @@ -0,0 +1,32 @@ +#!/bin/sh +# Shell script to extract the date given a release version and a .doap file + +if test "x$1" = "x" -o "x$2" = "x" -o ! -s "$2"; then + echo "Usage: $0 RELEASE-VERSION-NUMBER DOAP-FILE" >&2; + exit 1 +fi + +if ! grep '/dev/null ; then + echo "$2 does not look lika a .doap file" >&2; + exit 1 +fi + +if ! grep "$1" "$2" >/dev/null ; then + echo "$2 contains no reference to a version $1" >&2; + exit 1 +fi + +awk 'BEGIN {x=0} +{ +if ( $0 ~ // ) {x=1; chunk=""} +if (x==1) { + if ($0 ~ //) { chunk = chunk $0 } + if ($0 ~ //) { chunk = chunk $0 } +} +if ($0 ~ /<\/release>/) {x=0; print chunk} +}' < "$2" | \ +\ +grep ''"$1"'' | \ +\ +sed -e 's/^.*//' -e 's/<\/created>.*$//' + diff --git a/validate/common/gen-changelog.py b/validate/common/gen-changelog.py new file mode 100644 index 0000000..9bddaa0 --- /dev/null +++ b/validate/common/gen-changelog.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python + +import sys +import subprocess +import re + +# Makes a GNU-Style ChangeLog from a git repository +# Handles git-svn repositories also + +# Arguments : same as for git log +release_refs={} + +def process_commit(lines, files): + # DATE NAME + # BLANK LINE + # Subject + # BLANK LINE + # ... + # FILES + fileincommit = False + lines = [x.strip() for x in lines if x.strip() and not x.startswith('git-svn-id')] + files = [x.strip() for x in files if x.strip()] + for l in lines: + if l.startswith('* ') and ':' in l: + fileincommit = True + break + + top_line = lines[0] + print top_line.strip() + print + if not fileincommit: + for f in files: + print '\t* %s:' % f + for l in lines[1:]: + print '\t ', l + print + +def output_commits(): + cmd = ['git', 'log', '--pretty=format:--START-COMMIT--%H%n%ai %an <%ae>%n%n%s%n%b%n--END-COMMIT--', + '--date=short', '--name-only'] + + start_tag = find_start_tag() + + if start_tag is None: + cmd.extend(sys.argv[1:]) + else: + cmd.extend(["%s..HEAD" % (start_tag)]) + + p = subprocess.Popen(args=cmd, shell=False, stdout=subprocess.PIPE) + buf = [] + files = [] + filemode = False + for lin in p.stdout.readlines(): + if lin.startswith("--START-COMMIT--"): + if buf != []: + process_commit(buf, files) + hash = lin[16:].strip() + try: + rel = release_refs[hash] + print "=== release %d.%d.%d ===\n" % (int(rel[0]), int(rel[1]), int(rel[2])) + except: + pass + buf = [] + files = [] + filemode = False + elif lin.startswith("--END-COMMIT--"): + filemode = True + elif filemode == True: + files.append(lin) + else: + buf.append(lin) + if buf != []: + process_commit(buf, files) + +def get_rel_tags(): + # Populate the release_refs dict with the tags for previous releases + reltagre = re.compile("^([a-z0-9]{40}) refs\/tags\/[RELEASE-]*([0-9]+)[-_.]([0-9]+)[-_.]([0-9]+)") + + cmd = ['git', 'show-ref', '--tags', '--dereference'] + p = subprocess.Popen(args=cmd, shell=False, stdout=subprocess.PIPE) + for lin in p.stdout.readlines(): + match = reltagre.search (lin) + if match: + (sha, maj, min, nano) = match.groups() + release_refs[sha] = (maj, min, nano) + +def find_start_tag(): + starttagre = re.compile("^([a-z0-9]{40}) refs\/tags\/CHANGELOG_START") + cmd = ['git', 'show-ref', '--tags'] + p = subprocess.Popen(args=cmd, shell=False, stdout=subprocess.PIPE) + for lin in p.stdout.readlines(): + match = starttagre.search (lin) + if match: + return match.group(1) + return None + +if __name__ == "__main__": + get_rel_tags() + output_commits() diff --git a/validate/common/gettext.patch b/validate/common/gettext.patch new file mode 100644 index 0000000..682b905 --- /dev/null +++ b/validate/common/gettext.patch @@ -0,0 +1,12 @@ +--- po/Makefile.in.in.orig 2006-01-07 12:03:45.000000000 +0100 ++++ po/Makefile.in.in 2006-01-07 12:04:23.000000000 +0100 +@@ -11,6 +11,9 @@ + PACKAGE = @PACKAGE@ + VERSION = @VERSION@ + ++# thomas: add GETTEXT_PACKAGE substitution as used in Makevars ++GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ ++ + SHELL = /bin/sh + @SET_MAKE@ + diff --git a/validate/common/glib-gen.mak b/validate/common/glib-gen.mak new file mode 100644 index 0000000..ef93a5f --- /dev/null +++ b/validate/common/glib-gen.mak @@ -0,0 +1,44 @@ +# these are the variables your Makefile.am should set +# the example is based on the colorbalance interface + +#glib_enum_headers=$(colorbalance_headers) +#glib_enum_define=GST_COLOR_BALANCE +#glib_enum_prefix=gst_color_balance + +enum_headers=$(foreach h,$(glib_enum_headers),\n\#include \"$(h)\") + +# these are all the rules generating the relevant files +%-marshal.h: %-marshal.list + $(AM_V_GEN)glib-genmarshal --header --prefix=$(glib_enum_prefix)_marshal $^ > $*-marshal.h.tmp && \ + mv $*-marshal.h.tmp $*-marshal.h + +%-marshal.c: %-marshal.list + $(AM_V_GEN)echo "#include \"$*-marshal.h\"" >> $*-marshal.c.tmp && \ + glib-genmarshal --body --prefix=$(glib_enum_prefix)_marshal $^ >> $*-marshal.c.tmp && \ + mv $*-marshal.c.tmp $*-marshal.c + +%-enumtypes.h: $(glib_enum_headers) + $(AM_V_GEN)glib-mkenums \ + --fhead "#ifndef __$(glib_enum_define)_ENUM_TYPES_H__\n#define __$(glib_enum_define)_ENUM_TYPES_H__\n\n#include \n\nG_BEGIN_DECLS\n" \ + --fprod "\n/* enumerations from \"@filename@\" */\n" \ + --vhead "GType @enum_name@_get_type (void);\n#define GST_TYPE_@ENUMSHORT@ (@enum_name@_get_type())\n" \ + --ftail "G_END_DECLS\n\n#endif /* __$(glib_enum_define)_ENUM_TYPES_H__ */" \ + $^ > $@ + +%-enumtypes.c: $(glib_enum_headers) + @if test "x$(glib_enum_headers)" = "x"; then echo "ERROR: glib_enum_headers is empty, please fix Makefile"; exit 1; fi + $(AM_V_GEN)glib-mkenums \ + --fhead "#include \"$*-enumtypes.h\"\n$(enum_headers)" \ + --fprod "\n/* enumerations from \"@filename@\" */" \ + --vhead "GType\n@enum_name@_get_type (void)\n{\n static volatile gsize g_define_type_id__volatile = 0;\n if (g_once_init_enter (&g_define_type_id__volatile)) {\n static const G@Type@Value values[] = {" \ + --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \ + --vtail " { 0, NULL, NULL }\n };\n GType g_define_type_id = g_@type@_register_static (\"@EnumName@\", values);\n g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);\n }\n return g_define_type_id__volatile;\n}\n" \ + $^ > $@ + +# a hack rule to make sure .Plo files exist because they get include'd +# from Makefile's +.deps/%-marshal.Plo: + @touch $@ + +.deps/%-enumtypes.Plo: + @touch $@ diff --git a/validate/common/gst-autogen.sh b/validate/common/gst-autogen.sh new file mode 100644 index 0000000..c5798d0 --- /dev/null +++ b/validate/common/gst-autogen.sh @@ -0,0 +1,297 @@ +# a silly hack that generates autoregen.sh but it's handy +# Remove the old autoregen.sh first to create a new file, +# as the current one may be being read by the shell executing +# this script. +if [ -f "autoregen.sh" ]; then + rm autoregen.sh +fi +echo "#!/bin/sh" > autoregen.sh +echo "./autogen.sh $@ \$@" >> autoregen.sh +chmod +x autoregen.sh + +# helper functions for autogen.sh + +debug () +# print out a debug message if DEBUG is a defined variable +{ + if test ! -z "$DEBUG" + then + echo "DEBUG: $1" + fi +} + +version_get () +# based on the command's version output, set variables +# _MAJOR, _MINOR, _MICRO, _VERSION, using the given prefix as variable prefix +# +# arg 1: command binary name +# arg 2: (uppercased) variable name prefix +{ + COMMAND=$1 + VARPREFIX=`echo $2 | tr .,- _` + local ${VARPREFIX}_VERSION + + # strip everything that's not a digit, then use cut to get the first field + pkg_version=`$COMMAND --version|head -n 1|sed 's/^.*)[^0-9]*//'|cut -d' ' -f1` + debug "pkg_version $pkg_version" + # remove any non-digit characters from the version numbers to permit numeric + # comparison + pkg_major=`echo $pkg_version | cut -d. -f1 | sed s/[a-zA-Z\-].*//g` + pkg_minor=`echo $pkg_version | cut -d. -f2 | sed s/[a-zA-Z\-].*//g` + pkg_micro=`echo $pkg_version | cut -d. -f3 | sed s/[a-zA-Z\-].*//g` + test -z "$pkg_major" && pkg_major=0 + test -z "$pkg_minor" && pkg_minor=0 + test -z "$pkg_micro" && pkg_micro=0 + debug "found major $pkg_major minor $pkg_minor micro $pkg_micro" + eval ${VARPREFIX}_MAJOR=$pkg_major + eval ${VARPREFIX}_MINOR=$pkg_minor + eval ${VARPREFIX}_MICRO=$pkg_micro + eval ${VARPREFIX}_VERSION=$pkg_version +} + +version_compare () +# Checks whether the version of VARPREFIX is equal to or +# newer than the requested version +# arg1: VARPREFIX +# arg2: MAJOR +# arg3: MINOR +# arg4: MICRO +{ + VARPREFIX=`echo $1 | tr .,- _` + MAJOR=$2 + MINOR=$3 + MICRO=$4 + + eval pkg_major=\$${VARPREFIX}_MAJOR; + eval pkg_minor=\$${VARPREFIX}_MINOR; + eval pkg_micro=\$${VARPREFIX}_MICRO; + + #start checking the version + debug "version_compare: $VARPREFIX against $MAJOR.$MINOR.$MICRO" + + # reset check + WRONG= + + if [ ! "$pkg_major" -gt "$MAJOR" ]; then + debug "major: $pkg_major <= $MAJOR" + if [ "$pkg_major" -lt "$MAJOR" ]; then + debug "major: $pkg_major < $MAJOR" + WRONG=1 + elif [ ! "$pkg_minor" -gt "$MINOR" ]; then + debug "minor: $pkg_minor <= $MINOR" + if [ "$pkg_minor" -lt "$MINOR" ]; then + debug "minor: $pkg_minor < $MINOR" + WRONG=1 + elif [ "$pkg_micro" -lt "$MICRO" ]; then + debug "micro: $pkg_micro < $MICRO" + WRONG=1 + fi + fi + fi + if test ! -z "$WRONG"; then + debug "version_compare: $VARPREFIX older than $MAJOR.$MINOR.$MICRO" + return 1 + fi + debug "version_compare: $VARPREFIX equal to/newer than $MAJOR.$MINOR.$MICRO" + return 0 +} + + +version_check () +# check the version of a package +# first argument : package name (executable) +# second argument : optional path where to look for it instead +# third argument : source download url +# rest of arguments : major, minor, micro version +# all consecutive ones : suggestions for binaries to use +# (if not specified in second argument) +{ + PACKAGE=$1 + PKG_PATH=$2 + URL=$3 + MAJOR=$4 + MINOR=$5 + MICRO=$6 + + # for backwards compatibility, we let PKG_PATH=PACKAGE when PKG_PATH null + if test -z "$PKG_PATH"; then PKG_PATH=$PACKAGE; fi + debug "major $MAJOR minor $MINOR micro $MICRO" + VERSION=$MAJOR + if test ! -z "$MINOR"; then VERSION=$VERSION.$MINOR; else MINOR=0; fi + if test ! -z "$MICRO"; then VERSION=$VERSION.$MICRO; else MICRO=0; fi + + debug "major $MAJOR minor $MINOR micro $MICRO" + + for SUGGESTION in $PKG_PATH; do + COMMAND="$SUGGESTION" + + # don't check if asked not to + test -z "$NOCHECK" && { + printf " checking for $COMMAND >= $VERSION ... " + } || { + # we set a var with the same name as the package, but stripped of + # unwanted chars + VAR=`echo $PACKAGE | sed 's/-//g'` + debug "setting $VAR" + eval $VAR="$COMMAND" + return 0 + } + + which $COMMAND > /dev/null 2>&1 + if test $? -eq 1; + then + debug "$COMMAND not found" + continue + fi + + VARPREFIX=`echo $COMMAND | sed 's/-//g' | tr [:lower:] [:upper:]` + version_get $COMMAND $VARPREFIX + + version_compare $VARPREFIX $MAJOR $MINOR $MICRO + if test $? -ne 0; then + echo "found $pkg_version, not ok !" + continue + else + echo "found $pkg_version, ok." + # we set a var with the same name as the package, but stripped of + # unwanted chars + VAR=`echo $PACKAGE | sed 's/-//g'` + debug "setting $VAR" + eval $VAR="$COMMAND" + return 0 + fi + done + + echo "$PACKAGE not found !" + echo "You must have $PACKAGE installed to compile $package." + echo "Download the appropriate package for your distribution," + echo "or get the source tarball at $URL" + return 1; +} + +die_check () +{ + # call with $DIE + # if set to 1, we need to print something helpful then die + DIE=$1 + if test "x$DIE" = "x1"; + then + echo + echo "- Please get the right tools before proceeding." + echo "- Alternatively, if you're sure we're wrong, run with --nocheck." + exit 1 + fi +} + +autogen_options () +{ + if test "x$1" = "x"; then + return 0 + fi + + while test "x$1" != "x" ; do + optarg=`expr "x$1" : 'x[^=]*=\(.*\)'` + case "$1" in + --noconfigure) + NOCONFIGURE=defined + AUTOGEN_EXT_OPT="$AUTOGEN_EXT_OPT --noconfigure" + echo "+ configure run disabled" + shift + ;; + --nocheck) + AUTOGEN_EXT_OPT="$AUTOGEN_EXT_OPT --nocheck" + NOCHECK=defined + echo "+ autotools version check disabled" + shift + ;; + -d|--debug) + DEBUG=defined + AUTOGEN_EXT_OPT="$AUTOGEN_EXT_OPT --debug" + echo "+ debug output enabled" + shift + ;; + -h|--help) + echo "autogen.sh (autogen options) -- (configure options)" + echo "autogen.sh help options: " + echo " --noconfigure don't run the configure script" + echo " --nocheck don't do version checks" + echo " --debug debug the autogen process" + echo + echo " --with-autoconf PATH use autoconf in PATH" + echo " --with-automake PATH use automake in PATH" + echo + echo "Any argument either not in the above list or after a '--' will be " + echo "passed to ./configure." + exit 1 + ;; + --with-automake=*) + AUTOMAKE=$optarg + echo "+ using alternate automake in $optarg" + CONFIGURE_DEF_OPT="$CONFIGURE_DEF_OPT --with-automake=$AUTOMAKE" + shift + ;; + --with-autoconf=*) + AUTOCONF=$optarg + echo "+ using alternate autoconf in $optarg" + CONFIGURE_DEF_OPT="$CONFIGURE_DEF_OPT --with-autoconf=$AUTOCONF" + shift + ;; + --) shift ; break ;; + *) + echo "+ passing argument $1 to configure" + CONFIGURE_EXT_OPT="$CONFIGURE_EXT_OPT $1" + shift + ;; + esac + done + + for arg do CONFIGURE_EXT_OPT="$CONFIGURE_EXT_OPT $arg"; done + if test ! -z "$CONFIGURE_EXT_OPT" + then + echo "+ options passed to configure: $CONFIGURE_EXT_OPT" + fi +} + +toplevel_check () +{ + srcfile=$1 + test -f $srcfile || { + echo "You must run this script in the top-level $package directory" + exit 1 + } +} + +tool_run () +{ + tool=$1 + options=$2 + run_if_fail=$3 + echo "+ running $tool $options..." + $tool $options || { + echo + echo $tool failed + eval $run_if_fail + exit 1 + } +} + +install_git_hooks () +{ + if test -d .git; then + # install pre-commit hook for doing clean commits + for hook in pre-commit; do + if test ! \( -x .git/hooks/$hook -a -L .git/hooks/$hook \); then + echo "+ Installing git $hook hook" + rm -f .git/hooks/$hook + ln -s ../../common/hooks/$hook.hook .git/hooks/$hook || { + # if we couldn't create a symbolic link, try doing a plain cp + if cp common/hooks/pre-commit.hook .git/hooks/pre-commit; then + chmod +x .git/hooks/pre-commit; + else + echo "********** Couldn't install git $hook hook **********"; + fi + } + fi + done + fi +} diff --git a/validate/common/gst-glib-gen.mak b/validate/common/gst-glib-gen.mak new file mode 100644 index 0000000..0b0a5e9 --- /dev/null +++ b/validate/common/gst-glib-gen.mak @@ -0,0 +1,45 @@ +# these are the variables your Makefile.am should set +# the example is based on the colorbalance interface + +#glib_enum_headers=$(colorbalance_headers) +#glib_enum_define=GST_COLOR_BALANCE +#glib_gen_prefix=gst_color_balance +#glib_gen_basename=colorbalance + +enum_headers=$(foreach h,$(glib_enum_headers),\n\#include \"$(h)\") + +# these are all the rules generating the relevant files +$(glib_gen_basename)-marshal.h: $(glib_gen_basename)-marshal.list + $(AM_V_GEN)$(GLIB_GENMARSHAL) --header --prefix=$(glib_gen_prefix)_marshal $^ > $(glib_gen_basename)-marshal.h.tmp && \ + mv $(glib_gen_basename)-marshal.h.tmp $(glib_gen_basename)-marshal.h + +$(glib_gen_basename)-marshal.c: $(glib_gen_basename)-marshal.list + $(AM_V_GEN)echo "#include \"$(glib_gen_basename)-marshal.h\"" >> $(glib_gen_basename)-marshal.c.tmp && \ + $(GLIB_GENMARSHAL) --body --prefix=$(glib_gen_prefix)_marshal $^ >> $(glib_gen_basename)-marshal.c.tmp && \ + mv $(glib_gen_basename)-marshal.c.tmp $(glib_gen_basename)-marshal.c + +$(glib_gen_basename)-enumtypes.h: $(glib_enum_headers) + $(AM_V_GEN)$(GLIB_MKENUMS) \ + --fhead "#ifndef __$(glib_enum_define)_ENUM_TYPES_H__\n#define __$(glib_enum_define)_ENUM_TYPES_H__\n\n#include \n\nG_BEGIN_DECLS\n" \ + --fprod "\n/* enumerations from \"@filename@\" */\n" \ + --vhead "GType @enum_name@_get_type (void);\n#define GST_TYPE_@ENUMSHORT@ (@enum_name@_get_type())\n" \ + --ftail "G_END_DECLS\n\n#endif /* __$(glib_enum_define)_ENUM_TYPES_H__ */" \ + $^ > $@ + +$(glib_gen_basename)-enumtypes.c: $(glib_enum_headers) + @if test "x$(glib_enum_headers)" = "x"; then echo "ERROR: glib_enum_headers is empty, please fix Makefile"; exit 1; fi + $(AM_V_GEN)$(GLIB_MKENUMS) \ + --fhead "#include \"$(glib_gen_basename)-enumtypes.h\"\n$(enum_headers)" \ + --fprod "\n/* enumerations from \"@filename@\" */" \ + --vhead "GType\n@enum_name@_get_type (void)\n{\n static volatile gsize g_define_type_id__volatile = 0;\n if (g_once_init_enter (&g_define_type_id__volatile)) {\n static const G@Type@Value values[] = {" \ + --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \ + --vtail " { 0, NULL, NULL }\n };\n GType g_define_type_id = g_@type@_register_static (\"@EnumName@\", values);\n g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);\n }\n return g_define_type_id__volatile;\n}\n" \ + $^ > $@ + +# a hack rule to make sure .Plo files exist because they get include'd +# from Makefile's +.deps/%-marshal.Plo: + @touch $@ + +.deps/%-enumtypes.Plo: + @touch $@ diff --git a/validate/common/gst-indent b/validate/common/gst-indent new file mode 100755 index 0000000..b5e808f --- /dev/null +++ b/validate/common/gst-indent @@ -0,0 +1,55 @@ +#!/bin/sh +# +# Check that the code follows a consistant code style +# + +# Check for existence of indent, and error out if not present. +# On some *bsd systems the binary seems to be called gnunindent, +# so check for that first. + +version=`gnuindent --version 2>/dev/null` +if test "x$version" = "x"; then + version=`gindent --version 2>/dev/null` + if test "x$version" = "x"; then + version=`indent --version 2>/dev/null` + if test "x$version" = "x"; then + echo "GStreamer git pre-commit hook:" + echo "Did not find GNU indent, please install it before continuing." + exit 1 + else + INDENT=indent + fi + else + INDENT=gindent + fi +else + INDENT=gnuindent +fi + +case `$INDENT --version` in + GNU*) + ;; + default) + echo "GStreamer git pre-commit hook:" + echo "Did not find GNU indent, please install it before continuing." + echo "(Found $INDENT, but it doesn't seem to be GNU indent)" + exit 1 + ;; +esac + +INDENT_PARAMETERS="--braces-on-if-line \ + --case-brace-indentation0 \ + --case-indentation2 \ + --braces-after-struct-decl-line \ + --line-length80 \ + --no-tabs \ + --cuddle-else \ + --dont-line-up-parentheses \ + --continuation-indentation4 \ + --honour-newlines \ + --tab-size8 \ + --indent-level2 \ + --leave-preprocessor-space" + +$INDENT ${INDENT_PARAMETERS} $@ + diff --git a/validate/common/gst.supp b/validate/common/gst.supp new file mode 100644 index 0000000..6d491d8 --- /dev/null +++ b/validate/common/gst.supp @@ -0,0 +1,4016 @@ +### this file contains suppressions for valgrind when running +### the gstreamer unit tests +### it might be useful for wider use as well + +### syscall suppressions + +{ + + Memcheck:Param + clone(parent_tidptr) + fun:clone + fun:clone +} + +{ + + Memcheck:Param + clone(child_tidptr) + fun:clone + fun:clone +} + +{ + + Memcheck:Param + clone(tlsinfo) + fun:clone + fun:clone +} + +### glibc suppressions + +{ + + Memcheck:Cond + obj:/lib/ld-2.*.so + fun:dl_open_worker + obj:/lib/ld-2.*.so + fun:_dl_open + fun:dlopen_doit + obj:/lib/ld-2.*.so + fun:_dlerror_run + fun:dlopen + fun:g_module_open + fun:gst_plugin_load_file +} + +{ + + Memcheck:Cond + fun:strlen + fun:fillin_rpath + fun:_dl_init_paths + fun:dl_main + fun:_dl_sysdep_start + fun:_dl_start + obj:/lib64/ld-2.*.so + obj:* + obj:* +} + +{ + + Memcheck:Cond + fun:_dl_relocate_object + fun:dl_main + fun:_dl_sysdep_start + fun:_dl_start +} + +{ + + Memcheck:Cond + fun:* + fun:dl_open_worker + fun:_dl_catch_error + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen@@GLIBC_2.2.5 +} + +# glibc does not deallocate thread-local storage + +{ + + Memcheck:Leak + fun:calloc + fun:_dl_allocate_tls + fun:pthread_create@@* +} + +{ + + Memcheck:Leak + fun:calloc + fun:allocate_dtv + fun:_dl_allocate_tls +} + +# I get an extra stack entry on x86/dapper +{ + + Memcheck:Leak + fun:calloc + obj:/lib/ld-2.3.*.so + fun:_dl_allocate_tls + fun:pthread_create@@* +} + + +{ + + Memcheck:Cond + fun:strstr + fun:__pthread_initialize_minimal + obj:/lib/libpthread-*.so + obj:/lib/libpthread-*.so + fun:call_init + fun:_dl_init + obj:/lib/ld-*.so +} + +# a thread-related free problem in glibc from Edgard +{ + __libc_freeres_rw_acess + Memcheck:Addr4 + obj:* + obj:* + obj:* + obj:* + obj:* + fun:__libc_freeres +} + +{ + + Memcheck:Cond + obj:/lib/ld-2.3.*.so + obj:/lib/ld-2.3.*.so + obj:/lib/ld-2.3.*.so + obj:/lib/ld-2.3.*.so + obj:/lib/ld-2.3.*.so +} + +# g_module_open-related problems +{ + + Memcheck:Addr2 + fun:memcpy + fun:_dl_map_object_deps + fun:dl_open_worker + fun:_dl_catch_error + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen@@GLIBC_2.1 + fun:g_module_open + fun:gst_plugin_load_file + fun:gst_registry_scan_path_level + fun:gst_registry_scan_path_level + fun:gst_registry_scan_path_level + fun:init_post + fun:g_option_context_parse + fun:gst_init_check + fun:gst_init + fun:gst_check_init + fun:main +} + +{ + + Memcheck:Addr4 + fun:memcpy + fun:_dl_map_object_deps + fun:dl_open_worker + fun:_dl_catch_error + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen@@GLIBC_2.1 + fun:g_module_open + fun:gst_plugin_load_file + fun:gst_registry_scan_path_level + fun:gst_registry_scan_path_level + fun:gst_registry_scan_path_level + fun:init_post + fun:g_option_context_parse + fun:gst_init_check + fun:gst_init + fun:gst_check_init + fun:main +} + +{ + + Memcheck:Cond + obj:/lib/ld-2.3.*.so + obj:/lib/ld-2.3.*.so + obj:/lib/ld-2.3.*.so + fun:do_sym + fun:_dl_sym + fun:dlsym_doit + obj:/lib/ld-2.3.*.so + fun:_dlerror_run + fun:dlsym + fun:g_module_symbol + fun:g_module_open + fun:gst_plugin_load_file +} + +{ + + Memcheck:Cond + obj:/lib/ld-2.3.*.so + obj:/lib/ld-2.3.*.so + obj:/lib/ld-2.3.*.so + fun:dl_open_worker + obj:/lib/ld-2.3.*.so + fun:_dl_open + fun:dlopen_doit + obj:/lib/ld-2.3.*.so + fun:_dlerror_run + fun:dlopen@@GLIBC_2.1 + fun:g_module_open + fun:gst_plugin_load_file +} +{ + + Memcheck:Cond + obj:/lib/ld-2.3.*.so + fun:dl_open_worker + obj:/lib/ld-2.3.*.so + fun:_dl_open + fun:dlopen_doit + obj:/lib/ld-2.3.*.so + fun:_dlerror_run + fun:dlopen@@GLIBC_2.1 + fun:g_module_open + fun:gst_plugin_load_file + fun:gst_plugin_load_by_name + fun:gst_plugin_feature_load +} + +{ + + Memcheck:Leak + fun:malloc + obj:/lib/ld-2.3.*.so + fun:dl_open_worker + obj:/lib/ld-2.3.*.so + fun:_dl_open + fun:dlopen_doit + obj:/lib/ld-2.3.*.so + fun:_dlerror_run + fun:dlopen@@GLIBC_2.1 + fun:g_module_open + fun:gst_plugin_load_file + fun:gst_plugin_load_by_name +} + +{ + + Memcheck:Addr4 + obj:/lib/ld-2.3.*.so + obj:/lib/ld-2.3.*.so + obj:/lib/ld-2.3.*.so + obj:/lib/ld-2.3.*.so + obj:/lib/ld-2.3.*.so + obj:/lib/ld-2.3.*.so + obj:/lib/ld-2.3.*.so + fun:dl_open_worker + obj:/lib/ld-2.3.*.so + fun:_dl_open + fun:dlopen_doit + obj:/lib/ld-2.3.*.so +} + +{ + + Memcheck:Addr4 + obj:/lib/ld-2.3.*.so + obj:/lib/ld-2.3.*.so + obj:/lib/ld-2.3.*.so + obj:/lib/ld-2.3.*.so + obj:/lib/ld-2.3.*.so + obj:/lib/ld-2.3.*.so + fun:dl_open_worker + obj:/lib/ld-2.3.*.so + fun:_dl_open + fun:dlopen_doit + obj:/lib/ld-2.3.*.so + fun:_dlerror_run +} + +{ + + Memcheck:Addr4 + obj:/lib/ld-2.3.*.so + obj:/lib/ld-2.3.*.so + obj:/lib/ld-2.3.*.so + fun:dl_open_worker + obj:/lib/ld-2.3.*.so + fun:_dl_open + fun:dlopen_doit + obj:/lib/ld-2.3.*.so + fun:_dlerror_run + fun:dlopen@@GLIBC_2.1 + fun:g_module_open +} + +{ + + Memcheck:Addr4 + obj:/lib/ld-2.3.*.so + obj:/lib/ld-2.3.*.so + fun:dl_open_worker + obj:/lib/ld-2.3.*.so + fun:_dl_open + fun:dlopen_doit + obj:/lib/ld-2.3.*.so + fun:_dlerror_run + fun:dlopen@@GLIBC_2.1 + fun:g_module_open +} + +{ + + Memcheck:Addr4 + obj:/lib/ld-2.3.*.so + obj:/lib/ld-2.3.*.so + obj:/lib/ld-2.3.*.so + fun:do_sym + fun:_dl_sym + fun:dlsym_doit + obj:/lib/ld-2.3.*.so + fun:_dlerror_run + fun:dlsym + fun:g_module_symbol + fun:g_module_open +} + +{ + + Memcheck:Param + futex(uaddr2) + fun:pthread_once + obj:/lib/libc-2.3.*.so + obj:/lib/libc-2.3.*.so + fun:mbsnrtowcs + fun:vfprintf + fun:vsprintf + fun:sprintf + obj:/lib/libc-2.3.*.so + fun:tmpfile + fun:setup_pipe + fun:setup_messaging_with_key + fun:setup_messaging +} + +{ + + Memcheck:Addr4 + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libdl-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libdl-2.7.so + fun:dlopen + fun:g_module_open +} + +{ + + Memcheck:Addr4 + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libc-2.7.so + fun:_dl_sym + obj:/lib/i686/cmov/libdl-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libdl-2.7.so + fun:dlsym + fun:g_module_symbol + fun:g_module_open +} + +{ + + Memcheck:Addr4 + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libdl-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libdl-2.7.so + fun:dlopen + fun:g_module_open +} + +{ + + Memcheck:Addr4 + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libdl-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libdl-2.7.so + fun:dlopen + fun:g_module_open +} + +{ + + Memcheck:Addr4 + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libdl-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libdl-2.7.so + fun:dlopen + fun:g_module_open +} + +{ + + Memcheck:Addr4 + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libc-2.7.so + obj:/lib/ld-2.7.so + fun:__libc_dlopen_mode +} + +{ + + Memcheck:Addr4 + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libc-2.7.so + obj:/lib/ld-2.7.so + fun:__libc_dlopen_mode +} + +{ + + Memcheck:Addr4 + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libc-2.7.so + obj:/lib/ld-2.7.so + fun:__libc_dlopen_mode + obj:/lib/i686/cmov/libc-2.7.so + obj:/lib/i686/cmov/libc-2.7.so + obj:/lib/i686/cmov/libc-2.7.so + obj:/lib/i686/cmov/libc-2.7.so + obj:/lib/i686/cmov/libc-2.7.so + fun:iconv_open +} + +{ + + Memcheck:Addr4 + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libc-2.7.so + obj:/lib/ld-2.7.so + fun:__libc_dlopen_mode + obj:/lib/i686/cmov/libc-2.7.so + obj:/lib/i686/cmov/libc-2.7.so + obj:/lib/i686/cmov/libc-2.7.so + obj:/lib/i686/cmov/libc-2.7.so + obj:/lib/i686/cmov/libc-2.7.so + fun:iconv_open +} + +{ + + Memcheck:Addr8 + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/libc-2.7.so + obj:/lib/ld-2.7.so + fun:__libc_dlopen_mode + obj:/lib/libc-2.7.so + obj:/lib/libc-2.7.so + obj:/lib/libc-2.7.so + obj:/lib/libc-2.7.so + obj:/lib/libc-2.7.so + fun:iconv_open +} + +{ + + Memcheck:Addr8 + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/libc-2.7.so + obj:/lib/ld-2.7.so + fun:__libc_dlopen_mode + obj:/lib/libc-2.7.so + obj:/lib/libc-2.7.so + obj:/lib/libc-2.7.so + obj:/lib/libc-2.7.so + obj:/lib/libc-2.7.so + fun:iconv_open +} + +{ + + Memcheck:Addr4 + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libdl-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libdl-2.7.so + fun:dlopen +} + +{ + + Memcheck:Addr4 + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libdl-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libdl-2.7.so + fun:dlopen +} + +{ + + Memcheck:Addr8 + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/libc-2.7.so + obj:/lib/ld-2.7.so + fun:__libc_dlopen_mode +} + +{ + + Memcheck:Cond + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libdl-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libdl-2.7.so + fun:dlopen +} + +{ + + Memcheck:Cond + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libdl-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libdl-2.7.so + fun:dlopen +} + +{ + + Memcheck:Addr4 + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libdl-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libdl-2.7.so + fun:dlopen +} + +{ + + Memcheck:Addr4 + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libdl-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libdl-2.7.so + fun:dlopen +} + +{ + + Memcheck:Cond + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libdl-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libdl-2.7.so + fun:dlopen +} + +{ + + Memcheck:Cond + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libdl-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libdl-2.7.so + fun:dlopen +} + +{ + + Memcheck:Addr4 + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libdl-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libdl-2.7.so + fun:dlopen +} + +{ + + Memcheck:Addr4 + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libdl-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libdl-2.7.so + fun:dlopen +} + +{ + + Memcheck:Cond + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libdl-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libdl-2.7.so + fun:dlopen +} + +{ + + Memcheck:Cond + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libdl-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libdl-2.7.so + fun:dlopen +} + +{ + + Memcheck:Addr4 + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libdl-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/i686/cmov/libdl-2.7.so + fun:dlopen +} + +# suppression for a glibc bug: +# http://valgrind.org/docs/manual/faq.html#faq.exit_errors> +{ + + Memcheck:Free + fun:free + obj:*libc-*.so + fun:__libc_freeres + fun:* + fun:_Exit +} + +# same as above, just so it works for tpm on gutsy/x86-64 +{ + + Memcheck:Free + fun:free + fun:free_mem + fun:__libc_freeres +} + +# valgrind doesn't allow me to specify a suppression for Addr1, Addr2, Addr4 +# as Addr*, so 3 copies for that; and then 2 of each for that pesky memcpy +{ + + Memcheck:Addr1 + fun:_dl_signal_error + fun:_dl_map_object_deps + fun:dl_open_worker + fun:_dl_catch_error + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen@@GLIBC_2.1 + fun:g_module_open +} + +{ + + Memcheck:Addr2 + fun:_dl_signal_error + fun:_dl_map_object_deps + fun:dl_open_worker + fun:_dl_catch_error + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen@@GLIBC_2.1 + fun:g_module_open +} +{ + + Memcheck:Addr4 + fun:_dl_signal_error + fun:_dl_map_object_deps + fun:dl_open_worker + fun:_dl_catch_error + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen@@GLIBC_2.1 + fun:g_module_open +} + +{ + + Memcheck:Addr1 + fun:memcpy + fun:_dl_signal_error + fun:_dl_map_object_deps + fun:dl_open_worker + fun:_dl_catch_error + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen@@GLIBC_2.1 + fun:g_module_open +} + +{ + + Memcheck:Addr2 + fun:memcpy + fun:_dl_signal_error + fun:_dl_map_object_deps + fun:dl_open_worker + fun:_dl_catch_error + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen@@GLIBC_2.1 + fun:g_module_open +} +{ + + Memcheck:Addr4 + fun:memcpy + fun:_dl_signal_error + fun:_dl_map_object_deps + fun:dl_open_worker + fun:_dl_catch_error + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen@@GLIBC_2.1 + fun:g_module_open +} + +{ + + Memcheck:Addr8 + obj:/lib/ld-2.3.*.so + obj:/lib/ld-2.3.*.so + obj:/lib/ld-2.3.*.so + obj:/lib/ld-2.3.*.so + obj:/lib/ld-2.3.*.so + obj:/lib/ld-2.3.*.so + obj:/lib/ld-2.3.*.so + obj:/lib/libc-2.3.*.so + obj:/lib/ld-2.3.*.so + fun:_dl_open + obj:/lib/libdl-2.3.*.so + obj:/lib/ld-2.3.*.so +} + +{ + + Memcheck:Cond + obj:/lib/ld-2.3.*.so + obj:/lib/libc-2.3.*.so + obj:/lib/ld-2.3.*.so + fun:_dl_open + obj:/lib/libdl-2.3.*.so + obj:/lib/ld-2.3.*.so + obj:/lib/libdl-2.3.*.so + fun:dlopen + fun:g_module_open + fun:gst_plugin_load_file + fun:gst_plugin_load_by_name + fun:gst_plugin_feature_load +} + +{ + + Memcheck:Addr4 + obj:/lib/ld-2.3.6.so + obj:/lib/ld-2.3.6.so + obj:/lib/tls/i686/cmov/libc-2.3.6.so + obj:/lib/ld-2.3.6.so + fun:_dl_open + obj:/lib/tls/i686/cmov/libdl-2.3.6.so + obj:/lib/ld-2.3.6.so + obj:/lib/tls/i686/cmov/libdl-2.3.6.so + fun:dlopen +} + +{ + + Memcheck:Cond + obj:/lib/ld-2.3.6.so + obj:/lib/tls/i686/cmov/libc-2.3.6.so + obj:/lib/ld-2.3.6.so + fun:_dl_open + obj:/lib/tls/i686/cmov/libdl-2.3.6.so + obj:/lib/ld-2.3.6.so + obj:/lib/tls/i686/cmov/libdl-2.3.6.so + fun:dlopen +} + +{ + + Memcheck:Cond + obj:/lib/ld-2.3.6.so + obj:/lib/ld-2.3.6.so + obj:/lib/ld-2.3.6.so + obj:/lib/tls/i686/cmov/libc-2.3.6.so + obj:/lib/ld-2.3.6.so + fun:_dl_open + obj:/lib/tls/i686/cmov/libdl-2.3.6.so + obj:/lib/ld-2.3.6.so + obj:/lib/tls/i686/cmov/libdl-2.3.6.so + fun:dlopen +} + +### glib suppressions +{ + + Memcheck:Cond + fun:g_parse_debug_string + obj:/usr/lib*/libglib-2.0.so.* + fun:g_slice_alloc + fun:g_slice_alloc0 +} + +{ + + Memcheck:Leak + fun:*alloc + ... + fun:g_type_init* + fun:init_pre* +} + +{ + + Memcheck:Leak + fun:*alloc + ... + fun:g_type_register_fundamental +} + +{ + + Memcheck:Leak + fun:malloc + fun:realloc + fun:g_realloc + fun:type_node_any_new_W +} + +{ + + Memcheck:Leak + fun:realloc + fun:g_realloc + fun:type_node_any_new_W +} + +{ + + Memcheck:Leak + fun:calloc + fun:g_malloc0 + fun:g_type_class_ref +} + +{ + + Memcheck:Leak + fun:malloc + fun:realloc + fun:g_realloc + fun:type_add_flags_W +} + +{ + + Memcheck:Leak + fun:calloc + fun:g_malloc0 + fun:type_add_flags_W +} + +#pthread memleaks + +{ + Thread creation leak + Memcheck:Leak + fun:calloc + fun:allocate_dtv + fun:_dl_allocate* + fun:_dl_allocate* + fun:__pthread_initialize_minimal +} + +{ + Thread management leak + Memcheck:Leak + fun:calloc + fun:allocate_dtv + fun:_dl_allocate* + fun:_dl_allocate* + fun:__pthread_* +} + +{ + Thread management leak 2 + Memcheck:Leak + fun:memalign + fun:_dl_allocate* + fun:_dl_allocate* + fun:__pthread_* +} + +{ + pthread_create Syscall param write(buf) points to uninitialised byte(s) + Memcheck:Param + write(buf) + fun:pthread_create@@GLIBC_2.2.5 + fun:g_thread_create* + +} + +# nss_parse_* memleak (used by g_option_context_parse) +{ + nss_parse_* memleak + Memcheck:Leak + fun:malloc + fun:nss_parse_service_list + fun:__nss_database_lookup +} + +# liboil suppressions +{ + + Memcheck:Value8 + obj:/usr/lib/liboil-0.3.so.0.1.0 + obj:/usr/lib/liboil-0.3.so.0.1.0 + obj:/usr/lib/liboil-0.3.so.0.1.0 + fun:oil_cpu_fault_check_try + fun:oil_test_check_impl + fun:oil_class_optimize + fun:oil_optimize_all + fun:oil_init +} + +{ + + Memcheck:Addr8 + obj:/lib/ld-2.3.6.so +} + +{ + + Memcheck:Param + futex(uaddr2) + fun:pthread_once + obj:/lib/libc-2.3.6.so + obj:/lib/libc-2.3.6.so + fun:setlocale + fun:init_pre + fun:g_option_context_parse + fun:gst_init_check + fun:gst_init + fun:gst_check_init + fun:main +} + +{ + + Memcheck:Cond + obj:/lib/ld-2.3.6.so + obj:/lib/ld-2.3.6.so + fun:_dl_open + obj:/lib/libdl-2.3.6.so + obj:/lib/ld-2.3.6.so + obj:/lib/libdl-2.3.6.so + fun:dlopen + fun:g_module_open + fun:gst_plugin_load_file +} +# this exists in a bunch of different variations, hence the short tail/trace +{ + + Memcheck:Addr4 + obj:/lib/ld-2.4.so + obj:/lib/ld-2.4.so +} +{ + + Memcheck:Addr8 + obj:/lib/ld-2.4.so + obj:/lib/ld-2.4.so +} + +# More edgy suppressions (Mike) +{ + + Memcheck:Cond + obj:/lib/ld-2.4.so + obj:/lib/ld-2.4.so + obj:/lib/ld-2.4.so + obj:/lib/ld-2.4.so + fun:dlopen_doit + obj:/lib/ld-2.4.so + fun:_dlerror_run + fun:dlopen@@GLIBC_2.1 +} + +{ + + Memcheck:Cond + obj:/lib/ld-2.4.so + obj:/lib/ld-2.4.so + obj:/lib/ld-2.4.so + obj:/lib/ld-2.4.so + obj:/lib/ld-2.4.so + obj:/lib/ld-2.4.so + fun:dlopen_doit + obj:/lib/ld-2.4.so + fun:_dlerror_run + fun:dlopen@@GLIBC_2.1 +} + +{ + + Memcheck:Cond + obj:/lib/ld-2.4.so + obj:/lib/ld-2.4.so + obj:/lib/ld-2.4.so + fun:do_sym + fun:_dl_sym +} + +# This one's overly general, but there's zero other information in the stack +# trace - just these five lines! +{ + + Memcheck:Cond + obj:/lib/ld-2.4.so + obj:/lib/ld-2.4.so + obj:/lib/ld-2.4.so + obj:/lib/ld-2.4.so + obj:/lib/ld-2.4.so +} + +{ + + Memcheck:Leak + fun:calloc + obj:/lib/ld-2.4.so + fun:_dl_allocate_tls + fun:pthread_create@@GLIBC_2.1 +} + +# TLS leaks for feisty/x86 +{ + + Memcheck:Leak + fun:calloc + fun:allocate_dtv + fun:_dl_allocate_tls + fun:pthread_create@@GLIBC_2.1 +} + +{ + + Memcheck:Leak + fun:calloc + obj:/usr/lib/libcdio.so.6.0.1 + fun:cdio_open_am_linux + obj:/usr/lib/libcdio.so.6.0.1 + fun:cdio_open_am +} + +{ + + Memcheck:Addr8 + obj:/lib/ld-2.5.so +} + +{ + + Memcheck:Cond + fun:snd_pcm_direct_shm_create_or_connect + fun:snd_pcm_dsnoop_open + fun:_snd_pcm_dsnoop_open + obj:/*lib/libasound.so.2.0.0 + obj:/*lib/libasound.so.2.0.0 + fun:snd_pcm_open_slave + fun:_snd_pcm_plug_open + obj:/*lib/libasound.so.2.0.0 + fun:snd_pcm_open_slave + fun:_snd_pcm_asym_open + obj:/*lib/libasound.so.2.0.0 + obj:/*lib/libasound.so.2.0.0 +} + +{ + + Memcheck:Cond + fun:snd*_pcm_hw_param_set_near +} + +{ + + Memcheck:Cond + ... + fun:snd*_pcm_hw_param_set_near +} + +{ + + Memcheck:Cond + obj:/*lib/libasound.so.2.0.0 + obj:/*lib/libasound.so.2.0.0 + fun:snd_pcm_close + obj:/*lib/libasound.so.2.0.0 +} +{ + + Memcheck:Cond + fun:snd_pcm_direct_shm_create_or_connect + fun:snd_pcm_dmix_open + fun:_snd_pcm_dmix_open + obj:/*lib/libasound.so.2.0.0 + obj:/*lib/libasound.so.2.0.0 + fun:snd_pcm_open_slave + fun:_snd_pcm_softvol_open + obj:/*lib/libasound.so.2.0.0 + fun:snd_pcm_open_slave + fun:_snd_pcm_plug_open + obj:/*lib/libasound.so.2.0.0 + fun:snd_pcm_open_slave + fun:_snd_pcm_asym_open + obj:/*lib/libasound.so.2.0.0 + obj:/*lib/libasound.so.2.0.0 +} +{ + + Memcheck:Leak + fun:malloc + fun:strdup + fun:snd_dlobj_cache_add + obj:/*lib/libasound.so.2.0.0 + fun:snd_pcm_open_slave + fun:snd_pcm_dsnoop_open + fun:_snd_pcm_dsnoop_open + obj:/*lib/libasound.so.2.0.0 + obj:/*lib/libasound.so.2.0.0 + fun:snd_pcm_open_slave + fun:_snd_pcm_plug_open + obj:/*lib/libasound.so.2.0.0 + fun:snd_pcm_open_slave + fun:_snd_pcm_asym_open + obj:/*lib/libasound.so.2.0.0 + obj:/*lib/libasound.so.2.0.0 +} +# Catch about 15 variations on inserting info into an ALSA +# internal cache +{ + + Memcheck:Leak + fun:malloc + ... + fun:snd*_dlobj_cache_add + obj:/*lib*/libasound.so.2.0.0 +} + +{ + + Memcheck:Leak + fun:*alloc + ... + fun:snd_pcm_open_conf +} + +{ + + Memcheck:Leak + fun:*alloc + obj:/*lib*/libasound.so.2.0.0 + ... + fun:snd_config_hook_load +} + +{ + + Memcheck:Leak + fun:*alloc + obj:/*lib*/libasound.so.2.0.0 + ... + fun:snd_config_update_r + fun:snd_config_update +} +{ + + Memcheck:Leak + fun:*alloc + fun:strdup + ... + fun:snd_config_update_r + fun:snd_config_update +} +{ + + Memcheck:Leak + fun:*alloc + fun:_dl_close_worker + ... + fun:snd_config_searcha_hooks +} + +{ + + Memcheck:Leak + fun:malloc + obj:/lib/libc*.so + fun:__nss_database_lookup + obj:* + obj:* + fun:getgrnam_r + fun:getgrnam + fun:snd_pcm_direct_parse_open_conf +} + +{ + + Memcheck:Leak + fun:calloc + fun:_XCBInitDisplayLock + fun:XOpenDisplay +} + +# GConf internal initialisations related to getting the default client. +{ + + Memcheck:Leak + fun:calloc + fun:g_malloc0 + fun:ORBit_alloc_tcval + obj:/usr/lib/libORBit-2.so.* + fun:ORBit_demarshal_IOR + fun:ORBit_demarshal_object + fun:CORBA_ORB_string_to_object + obj:/usr/lib/libgconf-2.so.* + fun:gconf_get_current_lock_holder + fun:gconf_activate_server + obj:/usr/lib/libgconf-2.so.* + obj:/usr/lib/libgconf-2.so.* + fun:gconf_engine_get_default +} +{ + + Memcheck:Leak + fun:calloc + fun:g_malloc0 + fun:ORBit_alloc_tcval + obj:* + fun:PortableServer_POA_servant_to_reference + fun:* + fun:* + fun:* + fun:gconf_engine_get_default +} +{ + + Memcheck:Leak + fun:calloc + fun:g_malloc0 + fun:ORBit_alloc_tcval + obj:/usr/lib/libORBit-2.so.* + fun:ORBit_demarshal_IOR + fun:ORBit_demarshal_object + fun:CORBA_ORB_string_to_object + obj:/usr/lib/libgconf-2.so.* + fun:gconf_get_current_lock_holder + fun:gconf_activate_server + obj:/usr/lib/libgconf-2.so.* + obj:/usr/lib/libgconf-2.so.* + fun:gconf_engine_get_default +} +{ + + Memcheck:Leak + fun:calloc + fun:g_malloc0 + fun:ORBit_alloc* + fun:* + fun:ORBit_demarshal_IOR + fun:ORBit_demarshal_object + fun:ORBit_demarshal_value + fun:* + fun:ORBit_small_invoke_stub + fun:ConfigServer_get_default_database + fun:* + fun:gconf_engine_get_default +} +{ + + Memcheck:Leak + fun:calloc + fun:g_malloc0 + fun:ORBit_alloc* + fun:* + fun:IOP_generate_profiles + fun:ORBit_marshal_object + fun:ORBit_marshal_value + fun:* + fun:ORBit_small_invoke_stub + fun:ConfigServer_add_client + fun:* + fun:* + fun:gconf_engine_get_default +} +{ + + Memcheck:Leak + fun:calloc + fun:g_malloc0 + fun:ORBit_alloc_by_tc + fun:* + fun:PortableServer_POA_servant_to_reference + fun:* + fun:* + fun:* + fun:gconf_engine_get_default +} +{ + + Memcheck:Leak + fun:calloc + fun:g_malloc0 + fun:ORBit_alloc_by_tc + obj:/usr/lib/libORBit-2.so.* + fun:ORBit_demarshal_IOR + fun:ORBit_demarshal_object + fun:CORBA_ORB_string_to_object + obj:/usr/lib/libgconf-2.so.* + fun:gconf_get_current_lock_holder + fun:gconf_activate_server + obj:/usr/lib/libgconf-2.so.* + obj:/usr/lib/libgconf-2.so.* + fun:gconf_engine_get_default +} + +{ + + Memcheck:Leak + fun:calloc + fun:g_malloc0 + fun:ORBit_alloc* + fun:* + fun:ORBit_demarshal_IOR + fun:ORBit_demarshal_object + fun:* + fun:* + fun:gconf_activate_server +} + +# Some libORBit/bonobo initialisation stuff +{ + + Memcheck:Leak + fun:malloc + fun:g_malloc + fun:ORBit_alloc_string + fun:CORBA_string_dup + fun:Bonobo_ActivationEnvValue_set + fun:bonobo_activation_init_activation_env + fun:bonobo_activation_orb_init + fun:bonobo_activation_init +} +{ + + Memcheck:Leak + fun:calloc + fun:g_malloc0 + fun:ORBit_alloc* + fun:ORBit_small_alloc* + obj:/usr/lib/libORBit-2.so* + fun:PortableServer_POA_servant_to_reference + obj:/usr/lib/libbonobo-2.so* +} +{ + + Memcheck:Leak + fun:calloc + fun:g_malloc0 + fun:ORBit_alloc_tcval + fun:ORBit_small_allocbuf + fun:ORBit_adaptor_setup + obj:/usr/lib/libORBit-2.so* + fun:ORBit_POA_setup_root + fun:ORBit_init_internals + fun:CORBA_ORB_init +} +{ + + Memcheck:Leak + fun:calloc + fun:g_malloc0 + fun:ORBit_alloc_tcval + fun:ORBit_adaptor_setup + fun:* + fun:ORBit_POA_setup_root + fun:ORBit_init_internals + fun:CORBA_ORB_init +} +{ + + Memcheck:Leak + fun:calloc + fun:g_malloc0 + fun:ORBit_alloc* + fun:ORBit_small_allocbuf + fun:bonobo_activation_init_activation_env + fun:bonobo_activation_orb_init + fun:bonobo_activation_init +} + +# More GConf stuff from the FC5 buildbot, mostly variations on the +# above stack traces +{ + + Memcheck:Param + writev(vector[...]) + fun:writev + obj:/usr/lib/libORBit-2.so* + fun:link_connection_writev + fun:giop_send_buffer_write + obj:/usr/lib/libORBit-2.so* + fun:ORBit_small_invoke_stub + fun:ORBit_small_invoke_stub_n + fun:ORBit_c_stub_invoke + fun:ConfigServer_ping + fun:gconf_activate_server + obj:/usr/lib/libgconf-2.so* + obj:/usr/lib/libgconf-2.so* + fun:gconf_engine_get_default +} +{ + + Memcheck:Leak + fun:calloc + fun:g_malloc0 + fun:ORBit_alloc* + fun:ORBit_small_alloc* + obj:/usr/lib/libORBit-2.so* + fun:PortableServer_POA_servant_to_reference + obj:/usr/lib/libgconf-2.so* + obj:/usr/lib/libgconf-2.so* + obj:/usr/lib/libgconf-2.so* + fun:gconf_engine_get_default +} +{ + + Memcheck:Leak + fun:calloc + fun:g_malloc0 + fun:ORBit_alloc* + fun:ORBit_small_alloc + obj:/usr/lib/libORBit-2.so* + fun:ORBit_demarshal_IOR + fun:ORBit_demarshal_object + fun:CORBA_ORB_string_to_object + obj:/usr/lib/libgconf-2.so* + fun:gconf_get_current_lock_holder + fun:gconf_activate_server + obj:/usr/lib/libgconf-2.so* + obj:/usr/lib/libgconf-2.so* + fun:gconf_engine_get_default +} +{ + + Memcheck:Leak + fun:calloc + fun:g_malloc0 + fun:ORBit_alloc* + fun:ORBit_small_alloc* + obj:/usr/lib/libORBit-2.so* + fun:ORBit_demarshal_IOR + fun:ORBit_demarshal_object + fun:CORBA_ORB_string_to_object + obj:/usr/lib/libgconf-2.so* + fun:gconf_get_current_lock_holder + fun:gconf_activate_server + obj:/usr/lib/libgconf-2.so* + obj:/usr/lib/libgconf-2.so* + fun:gconf_engine_get_default +} +{ + + Memcheck:Leak + fun:calloc + fun:g_malloc0 + fun:ORBit_alloc* + fun:ORBit_small_alloc* + obj:/usr/lib/libORBit-2.so* + fun:ORBit_demarshal_IOR + fun:ORBit_demarshal_object + fun:ORBit_demarshal_value + obj:/usr/lib/libORBit-2.so* + fun:ORBit_small_invoke_stub + fun:ORBit_small_invoke_stub_n + fun:ORBit_c_stub_invoke + fun:ConfigServer_get_default_database + obj:/usr/lib/libgconf-2.so* + fun:gconf_engine_get_default +} +{ + + Memcheck:Leak + fun:calloc + fun:g_malloc0 + fun:ORBit_alloc* + fun:ORBit_small_alloc* + obj:/usr/lib/libORBit-2.so* + fun:ORBit_OAObject_object_to_objkey + fun:IOP_generate_profiles + fun:ORBit_marshal_object + fun:ORBit_marshal_value + obj:/usr/lib/libORBit-2.so* + fun:ORBit_small_invoke_stub + fun:ORBit_small_invoke_stub_n + fun:ORBit_c_stub_invoke + fun:ConfigServer_add_client + obj:/usr/lib/libgconf-2.so* + obj:/usr/lib/libgconf-2.so* + fun:gconf_engine_get_default +} +{ + + Memcheck:Leak + fun:malloc + obj:*libc-*.so + fun:__nss_database_lookup + obj:* + obj:* + fun:getpwnam_r + obj:/usr/lib*/libglib-2.0.so.* + fun:g_get_home_dir +} +{ + + Memcheck:Leak + fun:malloc + obj:*libc-*.so + fun:__nss_database_lookup + obj:* + obj:* + fun:getpwnam_r + obj:/usr/lib*/libglib-2.0.so.* + fun:g_get_user_name +} +{ + + Memcheck:Leak + fun:malloc + obj:*libc-*.so + fun:__nss_database_lookup + obj:* + obj:* + fun:getpwnam_r + obj:/usr/lib*/libglib-2.0.so.* + fun:g_get_tmp_dir +} + +{ + + Memcheck:Leak + fun:malloc + obj:*libc-*.so + fun:__nss_database_lookup + obj:* + obj:* + fun:getpwnam_r + obj:/usr/lib*/libglib-2.0.so.0.* + fun:g_get_host_name +} + + +## Some Fontconfig errors. +{ + + Memcheck:Leak + fun:malloc + fun:FcPatternObjectInsertElt + fun:FcPatternObjectAddWithBinding + fun:FcPatternAppend + fun:FcEndElement + obj:/usr/lib/libexpat.so.* + obj:/usr/lib/libexpat.so.* + obj:/usr/lib/libexpat.so.* + obj:/usr/lib/libexpat.so.* + fun:XML_ParseBuffer + fun:FcConfigParseAndLoad + fun:FcConfigParseAndLoad + fun:FcParseInclude + fun:FcEndElement + obj:/usr/lib/libexpat.so.* + obj:/usr/lib/libexpat.so.* + obj:/usr/lib/libexpat.so.* + obj:/usr/lib/libexpat.so.* + fun:XML_ParseBuffer + fun:FcConfigParseAndLoad +} +{ + + Memcheck:Leak + fun:*alloc + ... + fun:FcInitLoadConfig +} + +# Issues with ubuntu Hardy, same crack as for previous ubuntus +{ + + Memcheck:Leak + fun:calloc + obj:* + fun:_dl_allocate_tls + fun:pthread_create@@* + obj:/usr/lib/libgthread* + fun:g_thread_* +} + +# I've made this version generic, so that it covers future modifications +# of library names +{ + + Memcheck:Leak + fun:calloc + obj:* + fun:_dl_allocate_tls + fun:pthread_create@@* + fun:g_thread_* +} + +# series of invalid read of size 4 in g_module_open for ubuntu +# hardy x86/32bit +{ + + Memcheck:Addr4 + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/tls/i686/cmov/libdl-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/tls/i686/cmov/libdl-2.7.so + fun:dlopen + fun:g_module_open + fun:gst_plugin_load_* +} + +{ + + Memcheck:Addr4 + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/tls/i686/cmov/libdl-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/tls/i686/cmov/libdl-2.7.so + fun:dlopen + fun:g_module_open + fun:gst_plugin_load_* +} + +{ + + Memcheck:Addr4 + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/tls/i686/cmov/libdl-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/tls/i686/cmov/libdl-2.7.so + fun:dlopen + fun:g_module_open + fun:gst_plugin_load_* +} + +{ + + Memcheck:Addr4 + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/tls/i686/cmov/libdl-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/tls/i686/cmov/libdl-2.7.so + fun:dlopen + fun:g_module_open + fun:gst_plugin_load_* +} + +{ + + Memcheck:Addr4 + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/tls/i686/cmov/libdl-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/tls/i686/cmov/libdl-2.7.so + fun:dlopen + fun:g_module_open + fun:gst_plugin_load* +} + +{ + + Memcheck:Addr4 + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/tls/i686/cmov/libc-2.7.so + fun:_dl_sym + obj:/lib/tls/i686/cmov/libdl-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/tls/i686/cmov/libdl-2.7.so + fun:dlsym + fun:g_module_symbol + fun:g_module_open + fun:gst_plugin_load_* +} + +# series of invalid read of size 8 in g_module_open for ubuntu +# hardy x86/64bit +{ + + Memcheck:Addr8 + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/libdl-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/libdl-2.7.so + fun:dlopen + fun:g_module_open +} + +{ + + Memcheck:Addr8 + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/libdl-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/libdl-2.7.so + fun:dlopen + fun:g_module_open +} + +{ + + Memcheck:Addr8 + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/libdl-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/libdl-2.7.so + fun:dlopen + fun:g_module_open +} + +{ + + Memcheck:Addr8 + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/libdl-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/libdl-2.7.so + fun:dlopen + fun:g_module_open +} + +{ + + Memcheck:Addr8 + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/libdl-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/libdl-2.7.so + fun:dlopen + fun:g_module_open +} + +{ + + Memcheck:Addr8 + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/libdl-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/libdl-2.7.so + fun:dlopen + fun:g_module_open +} + +{ + + Memcheck:Addr8 + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/libc-2.7.so + obj:/lib/libdl-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/libdl-2.7.so + fun:dlsym + fun:g_module_symbol + fun:g_module_open +} + +{ + + Memcheck:Addr4 + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/tls/i686/cmov/libc-2.7.so + obj:/lib/ld-2.7.so + fun:__libc_dlopen_mode + fun:__nss_lookup_function + obj:/lib/tls/i686/cmov/libc-2.7.so + fun:__nss_passwd_lookup + fun:getpwnam_r +} + +{ + + Memcheck:Addr4 + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/tls/i686/cmov/libc-2.7.so + obj:/lib/ld-2.7.so + fun:__libc_dlopen_mode + fun:__nss_lookup_function + obj:/lib/tls/i686/cmov/libc-2.7.so + fun:__nss_passwd_lookup + fun:getpwnam_r +} + +{ + + Memcheck:Addr4 + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/tls/i686/cmov/libc-2.7.so + obj:/lib/ld-2.7.so + fun:__libc_dlopen_mode + fun:__nss_lookup_function + obj:/lib/tls/i686/cmov/libnss_compat-2.7.so + fun:_nss_compat_getpwnam_r + fun:getpwnam_r +} + +{ + + Memcheck:Addr4 + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/tls/i686/cmov/libc-2.7.so + obj:/lib/ld-2.7.so + fun:__libc_dlopen_mode + fun:__nss_lookup_function + obj:/lib/tls/i686/cmov/libnss_compat-2.7.so + fun:_nss_compat_getpwnam_r + fun:getpwnam_r +} + +{ + + Memcheck:Addr8 + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/libc-2.7.so + obj:/lib/ld-2.7.so + fun:__libc_dlopen_mode + fun:__nss_lookup_function + obj:/lib/libc-2.7.so + fun:getpwnam_r +} + +## Leaks in ALSA (variations of leak from snd_config_load1) + +{ + + Memcheck:Leak + fun:calloc + fun:_snd_config_make + fun:_snd_config_make_add + fun:* + fun:* + fun:* + fun:* + fun:snd_config_load1 +} + +{ + + Memcheck:Leak + fun:calloc + fun:_snd_config_make + fun:_snd_config_make_add + fun:* + fun:* + fun:snd_config_load1 +} +{ + + Memcheck:Leak + fun:calloc + fun:_snd_config_make + fun:_snd_config_make_add + fun:* + fun:* + fun:* + fun:snd_config_load1 +} +{ + + Memcheck:Leak + fun:calloc + fun:_snd_config_make + fun:_snd_config_make_add + fun:* + fun:* + fun:* + fun:* + fun:* + fun:snd_config_load1 +} + +{ + + Memcheck:Leak + fun:calloc + fun:_snd_config_make + fun:_snd_config_make_add + fun:* + fun:* + fun:* + fun:* + fun:* + fun:* + fun:snd_config_load1 +} + +{ + + Memcheck:Leak + fun:calloc + fun:_snd_config_make + fun:_snd_config_make_add + fun:* + fun:* + fun:* + fun:* + fun:* + fun:* + fun:* + fun:snd_config_load1 +} +{ + + Memcheck:Leak + fun:calloc + fun:_snd_config_make + fun:_snd_config_make_add + fun:* + fun:* + fun:* + fun:* + fun:* + fun:* + fun:* + fun:* + fun:snd_config_load1 +} + +{ + + Memcheck:Leak + fun:malloc + fun:snd1_dlobj_cache_add + fun:snd_ctl_open_noupdate +} + +{ + + Memcheck:Leak + fun:malloc + fun:* + fun:snd1_dlobj_cache_add + fun:snd_ctl_open_noupdate +} + +{ + + Memcheck:Leak + fun:*alloc + fun:* + fun:* + fun:* + fun:snd_config_load1 +} + +{ + + Memcheck:Leak + fun:*alloc + fun:* + fun:* + fun:* + fun:* + fun:snd_config_load1 +} + +{ + + Memcheck:Leak + fun:*alloc + fun:* + fun:* + fun:* + fun:* + fun:* + fun:snd_config_load1 +} + +{ + + Memcheck:Leak + fun:*alloc + fun:* + fun:* + fun:* + fun:* + fun:* + fun:* + fun:snd_config_load1 +} + +{ + + Memcheck:Leak + fun:*alloc + fun:* + fun:* + fun:* + fun:* + fun:* + fun:* + fun:* + fun:snd_config_load1 +} + +{ + + Memcheck:Leak + fun:*alloc + fun:* + fun:* + fun:* + fun:* + fun:* + fun:* + fun:* + fun:* + fun:snd_config_load1 +} + +{ + + Memcheck:Leak + fun:*alloc + fun:* + fun:* + fun:* + fun:* + fun:* + fun:* + fun:* + fun:* + fun:* + fun:snd_config_load1 +} + +{ + + Memcheck:Leak + fun:*alloc + fun:* + fun:* + fun:* + fun:* + fun:* + fun:* + fun:* + fun:* + fun:* + fun:* + fun:snd_config_load1 +} + + +# The following are leaks of caps that need to be created dynamically +# in the type registration of the plugin (used for pad templates). + +{ + + Memcheck:Leak + fun:malloc + fun:g_malloc + fun:g_slice_alloc + fun:gst_caps_new_empty + fun:gst_caps_new_simple + fun:* + fun:g_type_class_ref + fun:gst_element_register +} + +{ + + Memcheck:Leak + fun:malloc + fun:g_malloc + fun:g_slice_alloc + fun:gst_caps_new_empty + fun:* + fun:* + fun:g_type_class_ref + fun:gst_element_register + fun:gst_ogm_parse_plugin_init + fun:plugin_init +} + +{ + + Memcheck:Leak + fun:malloc + fun:g_malloc + fun:g_slice_alloc + fun:gst_caps_new_empty + fun:gst_caps_copy + fun:gst_video_test_src_base_init + fun:g_type_class_ref + fun:gst_element_register +} + +{ + + Memcheck:Leak + fun:malloc + fun:g_malloc + fun:g_slice_alloc + fun:gst_caps_new_empty + fun:gst_caps_copy + fun:gst_video_test_src_getcaps + fun:gst_video_test_src_base_init + fun:g_type_class_ref + fun:gst_element_register +} + +{ + + Memcheck:Leak + fun:malloc + fun:g_malloc + fun:g_slice_alloc + fun:gst_caps_new_empty + fun:gst_ffmpegcsp_codectype_to_caps + fun:gst_ffmpegcolorspace_register + fun:plugin_init +} + +{ + + Memcheck:Leak + fun:malloc + fun:g_malloc + fun:g_slice_alloc + fun:gst_caps_new_empty + fun:gst_caps_copy + fun:gst_ffmpegcolorspace_register + fun:plugin_init +} + +{ + + Memcheck:Leak + fun:malloc + fun:g_malloc + fun:g_slice_alloc + fun:gst_caps_new_empty + fun:gst_caps_new_any + fun:gst_ffmpegdemux_register + fun:plugin_init +} + +{ + + Memcheck:Leak + fun:malloc + fun:g_malloc + fun:g_slice_alloc + fun:gst_caps_new_empty + fun:gst_caps_copy + fun:gst_audio_filter_class_add_pad_templates +} + +{ + + Memcheck:Leak + fun:realloc + fun:g_realloc + fun:g_ptr_array_maybe_expand + fun:g_ptr_array_add + fun:gst_caps_append + fun:gst_audio_filter_class_add_pad_templates +} + +{ + + Memcheck:Leak + fun:malloc + fun:realloc + fun:g_realloc + fun:g_ptr_array_maybe_expand + fun:g_ptr_array_add + fun:gst_caps_append + fun:gst_audio_filter_class_add_pad_templates +} + +{ + + Memcheck:Leak + fun:malloc + fun:realloc + fun:g_realloc + fun:g_ptr_array_maybe_expand + fun:g_ptr_array_add + fun:gst_caps_copy + fun:gst_audio_filter_class_add_pad_templates +} + +{ + + Memcheck:Leak + fun:malloc + fun:g_malloc + fun:g_slice_alloc + fun:g_ptr_array_sized_new + fun:gst_caps_new_empty + fun:gst_caps_copy + fun:gst_audio_filter_class_add_pad_templates +} +{ + + Memcheck:Leak + fun:malloc + fun:realloc + fun:g_realloc + fun:g_array_maybe_expand + fun:g_array_sized_new + fun:* + fun:* + fun:* + fun:gst_value_init_and_copy + fun:gst_structure_copy + fun:gst_caps_copy + fun:gst_audio_filter_class_add_pad_templates +} +{ + + Memcheck:Leak + fun:malloc + fun:realloc + fun:g_realloc + fun:g_array_maybe_expand + fun:g_array_sized_new + fun:* + fun:gst_structure_copy + fun:gst_caps_copy + fun:gst_audio_filter_class_add_pad_templates +} +{ + + Memcheck:Leak + fun:malloc + fun:g_malloc + fun:g_slice_alloc + fun:g_array_sized_new + fun:* + fun:gst_structure_copy + fun:gst_caps_copy + fun:gst_audio_filter_class_add_pad_templates +} + +{ + + Memcheck:Leak + fun:malloc + fun:g_malloc + fun:g_slice_alloc + fun:gst_caps_new_empty + fun:gst_riff_create_*_template_caps +} +{ + + Memcheck:Leak + fun:malloc + fun:realloc + fun:g_realloc + fun:* + fun:* + fun:* + fun:gst_structure_copy + fun:gst_caps_copy + fun:gst_caps_append + fun:gst_riff_create_*_template_caps +} +{ + + Memcheck:Leak + fun:malloc + fun:g_malloc + fun:g_slice_alloc + fun:g_array_sized_new + fun:* + fun:gst_structure_copy + fun:gst_caps_copy + fun:gst_caps_append + fun:gst_riff_create_*_template_caps +} + +## Leaks in pango (bilboed: gentoo unstable amd64) + +{ + + Memcheck:Leak + fun:*alloc + ... + fun:pango_layout_get_pixel_extents +} +{ + + Memcheck:Leak + fun:calloc + fun:g_malloc0 + fun:pango_language_from_string + fun:pango_language_get_default + fun:pango_context_init + fun:g_type_create_instance + fun:g_object_constructor + fun:g_object_newv + fun:g_object_new_valist + fun:g_object_new + fun:pango_font_map_create_context +} + +{ + + Memcheck:Leak + fun:calloc + fun:g_malloc0 + fun:pango_language_from_string +} + + +## Leak of everything allocated by gst-libav plugin init +{ + + Memcheck:Leak + fun:*alloc + ... + fun:gst_ffmpeg_cfg_init +} + +## Leak of GIO module through gnomevfs + +{ + + Memcheck:Leak + fun:malloc + fun:g_malloc + fun:* + fun:* + fun:g_type_create_instance + fun:* + fun:* + fun:* + fun:* + fun:g_io_module_new + fun:g_io_modules_load_all_in_directory + fun:* + fun:get_default_vfs +} + +## Conditional jump in getaddrinfo (bilboed, gentoo ~amd64, Dec 13 2008) +{ + + Memcheck:Cond + fun:gaih_inet + fun:getaddrinfo +} + +## Dynamic pad templates in mxfmux +{ + + Memcheck:Leak + fun:malloc + fun:g_malloc + fun:g_slice_alloc + fun:gst_caps_new_empty + fun:gst_caps_from_string + fun:mxf_*_init + fun:plugin_init +} + +## We don't know if ffmpeg frees this or not and better pass a copy for safety +{ + + Memcheck:Leak + fun:malloc + fun:g_malloc + fun:g_strdup + fun:gst_ffmpeg_cfg_fill_context + fun:gst_ffmpegenc_setcaps + fun:gst_pad_set_caps +} + +## Leak/overreads with glibc-2.10 + +{ + + Memcheck:Value8 + fun:do_sym + fun:dlsym_doit + fun:_dl_catch_error + fun:_dlerror_run + fun:dlsym +} +{ + + Memcheck:Cond + fun:do_sym + fun:dlsym_doit + fun:_dl_catch_error + fun:_dlerror_run + fun:dlsym +} + +{ + + Memcheck:Value8 + fun:dl_open_worker + fun:_dl_catch_error + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen* +} + +{ + + Memcheck:Value8 + fun:_dl_relocate_object + fun:dl_open_worker + fun:_dl_catch_error + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen* +} + +{ + + Memcheck:Value8 + fun:_dl_check_map_versions + fun:dl_open_worker + fun:_dl_catch_error + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen* +} + +{ + + Memcheck:Cond + fun:dl_open_worker + fun:_dl_catch_error + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen* +} + +{ + + Memcheck:Cond + fun:_dl_relocate_object + fun:dl_open_worker + fun:_dl_catch_error + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen* +} + +{ + + Memcheck:Cond + fun:_dl_check_map_versions + fun:dl_open_worker + fun:_dl_catch_error + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen* +} + +{ + + Memcheck:Cond + fun:_dl_map_object* + fun:dl_open_worker + fun:_dl_catch_error + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen* +} + +{ + + Memcheck:Value8 + fun:_dl_map_object* + fun:dl_open_worker + fun:_dl_catch_error + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen* +} + +{ + + Memcheck:Value8 + fun:_dl_check_caller + fun:dl_open_worker + fun:_dl_catch_error + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen* +} + +{ + + Memcheck:Cond + fun:_dl_check_caller + fun:dl_open_worker + fun:_dl_catch_error + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen* +} + +{ + + Memcheck:Value8 + obj:/lib*/libc-2.10.*.so + obj:/lib*/libc-2.10.*.so + fun:_vgnU_freeres +} +{ + + Memcheck:Cond + obj:/lib*/libc-2.10.*.so + obj:/lib*/libc-2.10.*.so + fun:_vgnU_freeres +} +{ + + Memcheck:Free + fun:free + obj:/lib*/libc-2.10.*.so + obj:/lib*/libc-2.10.*.so + fun:_vgnU_freeres +} + +{ + + Memcheck:Value8 + fun:_dl_fini + fun:__run_exit_handlers + fun:exit +} + +{ + + Memcheck:Cond + fun:_dl_fini + fun:__run_exit_handlers + fun:exit +} +{ + + Memcheck:Value8 + fun:_dl_sort_fini + fun:_dl_fini + fun:__run_exit_handlers + fun:exit +} + +{ + + Memcheck:Cond + fun:_dl_sort_fini + fun:_dl_fini + fun:__run_exit_handlers + fun:exit +} + +# glibc-2.10 dl overreads +{ + + Memcheck:Value8 + fun:_dl_fixup + fun:_dl_runtime_resolve +} +{ + + Memcheck:Cond + fun:_dl_fixup + fun:_dl_runtime_resolve +} + +{ + + Memcheck:Value8 + fun:_dl_lookup_symbol_x + fun:_dl_fixup + fun:_dl_runtime_resolve +} +{ + + Memcheck:Cond + fun:_dl_lookup_symbol_x + fun:_dl_fixup + fun:_dl_runtime_resolve +} +{ + + Memcheck:Value8 + fun:call_init + fun:_dl_init +} +{ + + Memcheck:Value8 + fun:_dl_init +} +{ + + Memcheck:Value8 + fun:do_lookup_x + fun:_dl_lookup_symbol_x + fun:_dl_relocate_object + fun:dl_main +} +{ + + Memcheck:Cond + fun:do_lookup_x + fun:_dl_lookup_symbol_x + fun:_dl_relocate_object + fun:dl_main +} +{ + + Memcheck:Value8 + fun:_dl_lookup_symbol_x + fun:_dl_relocate_object + fun:dl_main +} +{ + + Memcheck:Value8 + fun:_dl_relocate_object + fun:dl_main +} +{ + + Memcheck:Value8 + fun:dl_main + fun:_dl_sysdep_start + fun:_dl_start +} +{ + + Memcheck:Cond + fun:dl_main + fun:_dl_sysdep_start + fun:_dl_start +} + +{ + + Memcheck:Cond + fun:* + fun:do_lookup_x + fun:_dl_lookup_symbol_x + fun:_dl_relocate_object + fun:dl_main +} + +{ + + Memcheck:Value8 + fun:* + fun:do_lookup_x + fun:_dl_lookup_symbol_x + fun:_dl_relocate_object + fun:dl_main +} + +{ + + Memcheck:Value8 + fun:_dl_check_map_versions + fun:_dl_check_all_versions + fun:version_check_doit + fun:_dl_receive_error + fun:dl_main +} + +{ + + Memcheck:Cond + fun:_dl_check_map_versions + fun:_dl_check_all_versions + fun:version_check_doit + fun:_dl_receive_error + fun:dl_main +} + +{ + + Memcheck:Value8 + fun:_dl_check_all_versions + fun:version_check_doit + fun:_dl_receive_error + fun:dl_main +} + +{ + + Memcheck:Cond + fun:_dl_check_all_versions + fun:version_check_doit + fun:_dl_receive_error + fun:dl_main +} + +{ + + Memcheck:Value8 + fun:* + fun:_dl_check_map_versions + fun:_dl_check_all_versions + fun:version_check_doit + fun:_dl_receive_error + fun:dl_main +} +{ + + Memcheck:Cond + fun:* + fun:_dl_check_map_versions + fun:_dl_check_all_versions + fun:version_check_doit + fun:_dl_receive_error + fun:dl_main +} + +{ + + Memcheck:Value8 + fun:init_tls + fun:dl_main +} +{ + + Memcheck:Cond + fun:init_tls + fun:dl_main +} + +{ + + Memcheck:Cond + fun:_dl_map_object_deps + fun:dl_main +} +{ + + Memcheck:Value8 + fun:_dl_map_object_deps + fun:dl_main +} + +{ + + Memcheck:Value8 + fun:_dl_protect_relro + fun:_dl_relocate_object + fun:dl_main +} + +{ + + Memcheck:Value8 + fun:* + fun:do_lookup_x + fun:_dl_lookup_symbol_x + fun:_dl_relocate_object + fun:dl_main +} + +{ + + Memcheck:Value8 + fun:_dl_setup_hash + fun:_dl_map_object_from_fd + fun:_dl_map_object +} + +{ + + Memcheck:Value8 + fun:* + fun:_dl_new_object + fun:_dl_map_object_from_fd + fun:_dl_map_object +} + +{ + + Memcheck:Cond + fun:* + fun:_dl_new_object + fun:_dl_map_object_from_fd + fun:_dl_map_object +} + +{ + + Memcheck:Value8 + fun:openaux + fun:_dl_catch_error + fun:_dl_map_object_deps + fun:dl_main +} + +{ + + Memcheck:Value8 + fun:* + fun:_dl_map_object +} + +{ + + Memcheck:Cond + fun:* + fun:_dl_map_object +} + +{ + + Memcheck:Cond + fun:_dl_map_object + fun:openaux + fun:_dl_catch_error + fun:_dl_map_object_deps + fun:dl_main +} + +{ + + Memcheck:Value8 + fun:_dl_map_object + fun:openaux + fun:_dl_catch_error + fun:_dl_map_object_deps + fun:dl_main +} + +{ + + Memcheck:Cond + fun:* + fun:_dl_map_object + fun:openaux + fun:_dl_catch_error + fun:_dl_map_object_deps + fun:dl_main +} + +{ + + Memcheck:Value8 + fun:* + fun:open_path + fun:_dl_map_object + fun:openaux + fun:_dl_catch_error + fun:_dl_map_object_deps + fun:dl_main +} + +{ + + Memcheck:Cond + fun:* + fun:open_path + fun:_dl_map_object + fun:openaux + fun:_dl_catch_error + fun:_dl_map_object_deps + fun:dl_main +} + +{ + + Memcheck:Value8 + fun:_dl_map_object_from_fd + fun:_dl_map_object +} + +{ + + Memcheck:Cond + fun:_dl_map_object_from_fd + fun:_dl_map_object +} + +{ + + Memcheck:Value8 + fun:* + fun:_dl_new_object + fun:_dl_map_object_from_fd + fun:_dl_map_object +} + +{ + + Memcheck:Value8 + fun:_dl_new_object + fun:_dl_map_object_from_fd + fun:_dl_map_object +} + +{ + + Memcheck:Cond + fun:_dl_new_object + fun:_dl_map_object_from_fd + fun:_dl_map_object +} + +{ + + Memcheck:Value8 + fun:* + fun:_dl_name_match_p + fun:_dl_map_object +} + +{ + + Memcheck:Cond + fun:* + fun:* + fun:_dl_map_object +} + +{ + + Memcheck:Value8 + fun:* + fun:_dl_name_match_p + fun:_dl_check_map_versions + fun:_dl_check_all_versions +} + +{ + + Memcheck:Value8 + fun:* + fun:* + fun:do_lookup_x + fun:_dl_lookup_symbol_x +} + +{ + + Memcheck:Cond + fun:do_lookup_x + fun:_dl_lookup_symbol_x +} + +{ + + Memcheck:Value8 + fun:do_lookup_x + fun:_dl_lookup_symbol_x +} + +{ + + Memcheck:Value8 + fun:* + fun:do_lookup_x + fun:_dl_lookup_symbol_x +} +{ + + Memcheck:Cond + fun:* + fun:do_lookup_x + fun:_dl_lookup_symbol_x +} + +{ + + Memcheck:Value8 + fun:_dl_name_match_p + fun:_dl_map_object + fun:dl_open_worker +} +{ + + Memcheck:Cond + fun:_dl_name_match_p + fun:_dl_map_object + fun:dl_open_worker +} + +{ + + Memcheck:Value8 + fun:* + fun:_dl_name_match_p + fun:_dl_map_object + fun:dl_open_worker +} +{ + + Memcheck:Cond + fun:* + fun:_dl_name_match_p + fun:_dl_map_object + fun:dl_open_worker +} + +{ + + Memcheck:Value8 + fun:_dl_lookup_symbol_x + fun:_dl_relocate_object +} +{ + + Memcheck:Cond + fun:_dl_lookup_symbol_x + fun:_dl_relocate_object +} + +{ + + Memcheck:Value8 + fun:* + fun:* + fun:_dl_check_map_versions +} +{ + + Memcheck:Value8 + fun:* + fun:_dl_check_map_versions +} +{ + + Memcheck:Cond + fun:* + fun:* + fun:_dl_check_map_versions +} +{ + + Memcheck:Cond + fun:* + fun:_dl_check_map_versions +} +{ + + Memcheck:Value8 + fun:openaux +} +{ + + Memcheck:Value8 + fun:_dl_name_match_p + fun:_dl_map_object +} + +{ + + Memcheck:Cond + fun:_dl_close_worker + fun:_dl_close + fun:_dl_catch_error + fun:dlerror_run +} +{ + + Memcheck:Value8 + fun:_dl_close_worker + fun:_dl_close + fun:_dl_catch_error + fun:dlerror_run +} +{ + + Memcheck:Cond + fun:* + fun:_dl_close_worker + fun:_dl_close + fun:_dl_catch_error + fun:dlerror_run +} +{ + + Memcheck:Value8 + fun:* + fun:_dl_close_worker + fun:_dl_close + fun:_dl_catch_error + fun:dlerror_run +} + +{ + + Memcheck:Cond + fun:fillin_rpath + fun:_dl_init_paths + fun:dl_main +} +{ + + Memcheck:Value8 + fun:fillin_rpath + fun:_dl_init_paths + fun:dl_main +} +{ + + Memcheck:Cond + fun:* + fun:fillin_rpath + fun:_dl_init_paths + fun:dl_main +} +{ + + Memcheck:Value8 + fun:* + fun:fillin_rpath + fun:_dl_init_paths + fun:dl_main +} + +{ + + Memcheck:Cond + fun:_dl_map_object + fun:map_doit + fun:_dl_catch_error + fun:do_preload + fun:dl_main +} +{ + + Memcheck:Value8 + fun:_dl_map_object + fun:map_doit + fun:_dl_catch_error + fun:do_preload + fun:dl_main +} +{ + + Memcheck:Param + open(filename) + fun:open + fun:open_verify + fun:_dl_map_object + fun:map_doit + fun:_dl_catch_error + fun:do_preload + fun:dl_main +} + +{ + + Memcheck:Param + stat(file_name) + fun:_xstat + fun:open_path + fun:_dl_map_object + fun:openaux + fun:_dl_catch_error + fun:_dl_map_object_deps + fun:dl_main +} + +{ + + Memcheck:Value8 + fun:_dl_catch_error + fun:_dl_map_object_deps + fun:dl_open_worker +} + +{ + + Memcheck:Cond + fun:* + fun:_dl_map_object_deps + fun:dl_main +} +{ + + Memcheck:Value8 + fun:* + fun:_dl_map_object_deps + fun:dl_main +} + +{ + + Memcheck:Value8 + fun:* + fun:* + fun:_dl_map_object_deps + fun:dl_main +} + +# glibc-2.10 tls issues +{ + + Memcheck:Cond + fun:* + fun:init_tls + fun:dl_main +} +{ + + Memcheck:Value8 + fun:* + fun:init_tls + fun:dl_main +} +{ + + Memcheck:Cond + fun:* + fun:* + fun:init_tls + fun:dl_main +} +{ + + Memcheck:Value8 + fun:* + fun:* + fun:init_tls + fun:dl_main +} + +{ + + Memcheck:Cond + fun:_dl_allocate_tls_init + fun:dl_main +} +{ + + Memcheck:Value8 + fun:_dl_allocate_tls_init + fun:dl_main +} +{ + + Memcheck:Cond + fun:* + fun:_dl_allocate_tls_init + fun:dl_main +} +{ + + Memcheck:Value8 + fun:* + fun:_dl_allocate_tls_init + fun:dl_main +} + +{ + + Memcheck:Cond + fun:__tls* + obj:* + obj:* + fun:_vgnU_freeres +} + +{ + + Memcheck:Param + arch_prctl(arg2) + fun:init_tls +} +# GLib caching tmp/home directories (glibc-2.10 variants) +{ + + Memcheck:Cond + fun:* + fun:dl_open_worker + fun:* + fun:* + fun:* + fun:_dl_catch_error + fun:dlerror_run + fun:* + fun:__nss_lookup_function + fun:__nss_lookup + fun:getpwnam* +} +{ + + Memcheck:Value8 + fun:* + fun:dl_open_worker + fun:* + fun:* + fun:* + fun:_dl_catch_error + fun:dlerror_run + fun:* + fun:__nss_lookup_function + fun:__nss_lookup + fun:getpwnam* +} +{ + + Memcheck:Cond + fun:dl_open_worker + fun:* + fun:* + fun:do_dlopen + fun:* + fun:dlerror_run + fun:* + fun:__nss_lookup_function + fun:__nss_lookup + fun:getpwnam* +} +{ + + Memcheck:Value8 + fun:dl_open_worker + fun:* + fun:* + fun:do_dlopen + fun:* + fun:dlerror_run + fun:* + fun:__nss_lookup_function + fun:__nss_lookup + fun:getpwnam* +} + +{ + + Memcheck:Value8 + fun:_dl_add_to_slotinfo + fun:dl_main +} +{ + + Memcheck:Param + open(filename) + fun:open + fun:open_verify + fun:open_path + fun:_dl_map_object +} + + + +# GModule issues with glibc-2.10 +{ + + Memcheck:Value8 + fun:* + fun:* + fun:dlsym + fun:g_module_symbol +} +{ + + Memcheck:Value8 + fun:g_module_* + fun:gst_plugin* +} +{ + + Memcheck:Value8 + fun:* + fun:g_module_* + fun:gst_plugin* +} + +{ + + Memcheck:Value8 + fun:* + fun:* + fun:dlopen* + fun:g_module_open +} +{ + + Memcheck:Value8 + fun:* + fun:* + fun:* + fun:* + fun:* + fun:* + fun:* + fun:dlsym + fun:g_module_symbol +} + +{ + + Memcheck:Value8 + fun:* + fun:* + fun:* + fun:* + fun:* + fun:dlopen* + fun:g_module_open +} + +# Leak in GSlice +{ + + Memcheck:Value8 + fun:g_parse_debug_string + fun:slice_config_init + fun:g_slice_init_nomessage + fun:_g_slice_thread_init_nomessage + fun:g_thread_init_glib +} + +# 2.10 pthread issues +{ + + Memcheck:Value8 + fun:__pthread_initialize_minimal +} + +# glibc 2.11 conditional +{ + + Memcheck:Cond + fun:_dl_relocate_object + fun:dl_main + fun:_dl_sysdep_start + fun:_dl_start + obj:/lib64/ld-2.11.so +} + +# glibc 2.11 Leak + +{ + + Memcheck:Leak + fun:*alloc + fun:dl_open_worker + fun:_dl_catch_error + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen@@GLIBC_2.2.5 +} + +{ + + Memcheck:Leak + fun:*alloc + fun:_dl_* + fun:dl_open_worker + fun:_dl_catch_error + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen@@GLIBC_2.2.5 +} + +{ + + Memcheck:Leak + fun:*alloc + fun:_dl_* + fun:_dl_* + fun:_dl_* + fun:dl_open_worker + fun:_dl_catch_error + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen@@GLIBC_2.2.5 +} + +{ + + Memcheck:Leak + fun:*alloc + fun:* + fun:_dl_* + fun:openaux + fun:_dl_catch_error + fun:_dl_map_object_deps + fun:dl_open_worker + fun:_dl_catch_error + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen@@GLIBC_2.2.5 +} + +{ + + Memcheck:Leak + fun:*alloc + fun:* + fun:_dl_map_object + fun:dl_open_worker + fun:_dl_catch_error + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen@@GLIBC_2.2.5 +} + +{ + + Memcheck:Leak + fun:*alloc + fun:_dl_new_object + fun:_dl_map_object_from_fd + fun:_dl_map_object + fun:openaux + fun:_dl_catch_error + fun:_dl_map_object_deps + fun:dl_open_worker + fun:_dl_catch_error + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen@@GLIBC_2.2.5 +} + +{ + + Memcheck:Leak + fun:*alloc + fun:* + fun:_dl_* + fun:_dl_* + fun:_dl_* + fun:dl_open_worker + fun:_dl_catch_error + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen@@GLIBC_2.2.5 +} + +# glib type leaks +{ + + Memcheck:Leak + fun:*alloc + ... + fun:g_type_register_static +} + +# new registry system +# all of this will only be created once when loading registry. + +{ + + Memcheck:Leak + fun:*alloc + ... + fun:_priv_gst_registry_chunks_load_plugin +} + +# system-wide tags +# these tags are registered once + +{ + + Memcheck:Leak + fun:*alloc + fun:* + fun:* + fun:gst_tag_register + fun:_gst_tag_initialize +} + +# system-wide type classes that we keep referenced + +{ + + Memcheck:Leak + fun:*alloc + ... + fun:g_type_class_ref +} + +# leaking cached queries which are only initialized once +{ + + Memcheck:Leak + fun:*alloc + ... + fun:_gst_query_initialize + fun:init_post +} + +# macosx (leopard) library loader leak +{ + + Memcheck:Leak + fun:_Znwm + fun:_ZNSs4_Rep9_S_createEmmRKSaIcE + fun:_ZNSs12_S_constructIPKcEEPcT_S3_RKSaIcESt20forward_iterator_tag + fun:_ZNSsC2EPKcRKSaIcE + fun:_Z41__static_initialization_and_destruction_0ii + fun:_ZN16ImageLoaderMachO18doModInitFunctionsERKN11ImageLoader11LinkContextE +} + +# GObject type registration +{ + + Memcheck:Leak + fun:*alloc + ... + fun:_g_atomic_array_copy +} + +{ + + Memcheck:Leak + fun:*alloc + fun:getdelim + obj:*libselinux* +} + +{ + + Memcheck:Leak + fun:*alloc + ... + obj:*/sed +} + +{ + + Memcheck:Addr8 + ... + obj:*/sed +} + +# GLib 2.23 interface vtable +{ + + Memcheck:Leak + fun:*alloc + ... + fun:g_type_add_interface_static +} + +{ + + Memcheck:Leak + fun:*alloc + obj:*/dash +} + +# libtool/gentoo fake leak +# it actually runs bash and valgrind complains +{ + + Memcheck:Leak + fun:*alloc + obj:/bin/bash +} + +{ + + Memcheck:Leak + fun:*alloc + ... + fun:_gst_plugin_loader_client_run + fun:main +} + +{ + + Memcheck:Cond + fun:*strcasecmp* + ... + fun:__dcigettext +} + +{ + + Memcheck:Value8 + fun:*strcasecmp* + ... + fun:__dcigettext +} + +{ + + Memcheck:Leak + fun:malloc + ... + fun:gst_poll_new + fun:gst_poll_new_timer + fun:gst_system_clock_init +} + +{ + + Memcheck:Leak + fun:calloc + ... + fun:gobject_init_ctor +} + +{ + + Memcheck:Leak + fun:malloc + ... + fun:g_quark_from*_string +} + +{ + + Memcheck:Param + timer_create(evp) + fun:timer_create@@GLIBC_2.3.3 +} + +{ + closures aren't valgrind friendly (bgo#739850) + Memcheck:Leak + match-leak-kinds: possible + fun:calloc + ... + fun:g_cclosure_new +} + +{ + closures aren't valgrind friendly (bgo#739850) + Memcheck:Leak + match-leak-kinds: possible + fun:malloc + ... + fun:g_closure_add_invalidate_notifier +} + +{ + closures aren't valgrind friendly (bgo#739850) + Memcheck:Leak + match-leak-kinds: possible + fun:calloc + ... + fun:g_closure_new_simple +} + +{ + glib/giomodules2 (from libsoup.supp) + Memcheck:Leak + ... + fun:_g_io_module_get_default +} + +{ + + Memcheck:Addr8 + fun:__GI___strncasecmp_l + fun:____strtod_l_internal + fun:gst_value_deserialize_double +} diff --git a/validate/common/gstdoc-scangobj b/validate/common/gstdoc-scangobj new file mode 100755 index 0000000..4ad9786 --- /dev/null +++ b/validate/common/gstdoc-scangobj @@ -0,0 +1,1617 @@ +#!/usr/bin/env perl +# -*- cperl -*- +# +# gtk-doc - GTK DocBook documentation generator. +# Copyright (C) 1998 Damon Chaplin +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +# + +# +# This gets information about object hierarchies and signals +# by compiling a small C program. CFLAGS and LDFLAGS must be +# set appropriately before running this script. +# + +use Getopt::Long; + +my $GTK_DOC_PREFIX=`pkg-config --variable prefix gtk-doc`; +if ($GTK_DOC_PREFIX) { + chomp $GTK_DOC_PREFIX; + #print "Adding $GTK_DOC_PREFIX/share/gtk-doc/data to \@INC\n"; + unshift @INC, "$GTK_DOC_PREFIX/share/gtk-doc/data"; +} else { + unshift @INC, '/usr/share/gtk-doc/data'; +} +require "gtkdoc-common.pl"; + +# Options + +# name of documentation module +my $MODULE; +my $OUTPUT_DIR; +my $INSPECT_DIR; +my $VERBOSE; +my $PRINT_VERSION; +my $PRINT_HELP; +my $TYPE_INIT_FUNC="g_type_init ()"; + +# --nogtkinit is deprecated, as it is the default now anyway. +%optctl = (module => \$MODULE, + source => \$SOURCE, + types => \$TYPES_FILE, + nogtkinit => \$NO_GTK_INIT, + 'type-init-func' => \$TYPE_INIT_FUNC, + 'output-dir' => \$OUTPUT_DIR, + 'inspect-dir' => \$INSPECT_DIR, + 'verbose' => \$VERBOSE, + 'version' => \$PRINT_VERSION, + 'help' => \$PRINT_HELP); + +GetOptions(\%optctl, "module=s", "source=s", "types:s", "output-dir:s", "inspect-dir:s", "nogtkinit", "type-init-func:s", "verbose", "version", "help"); + +if ($NO_GTK_INIT) { + # Do nothing. This just avoids a warning. + # the option is not used anymore +} + +if ($PRINT_VERSION) { + print "1.5\n"; + exit 0; +} + +if (!$MODULE) { + $PRINT_HELP = 1; +} + +if ($PRINT_HELP) { + print <$MODULE-scan.c") || die "Cannot open $MODULE-scan.c: $!\n"; + +my $old_signals_filename = "$OUTPUT_DIR/$MODULE.signals"; +my $new_signals_filename = "$OUTPUT_DIR/$MODULE.signals.new"; +my $old_hierarchy_filename = "$OUTPUT_DIR/$MODULE.hierarchy"; +my $new_hierarchy_filename = "$OUTPUT_DIR/$MODULE.hierarchy.new"; +my $old_interfaces_filename = "$OUTPUT_DIR/$MODULE.interfaces"; +my $new_interfaces_filename = "$OUTPUT_DIR/$MODULE.interfaces.new"; +my $old_prerequisites_filename = "$OUTPUT_DIR/$MODULE.prerequisites"; +my $new_prerequisites_filename = "$OUTPUT_DIR/$MODULE.prerequisites.new"; +my $old_args_filename = "$OUTPUT_DIR/$MODULE.args"; +my $new_args_filename = "$OUTPUT_DIR/$MODULE.args.new"; + +my $debug_log="g_message"; +if (!defined($VERBOSE) or $VERBOSE eq "0") { + $debug_log="//$debug_log"; +} + +# write a C program to scan the types + +$includes = ""; +@types = (); +@impl_types = (); + +for () { + if (/^#include/) { + $includes .= $_; + } elsif (/^%/) { + next; + } elsif (/^\s*$/) { + next; + } elsif (/^type:(.*)$/) { + $t = $1; + chomp $t; + push @impl_types, $t; + } else { + chomp; + push @types, $_; + } +} + +$ntypes = @types + @impl_types + 1; + +print OUTPUT < +#include +#include +#include + +#include +EOT + +if ($includes) { + print OUTPUT $includes; +} else { + for (@types) { + print OUTPUT "extern GType $_ (void);\n"; + } +} + +print OUTPUT < +#endif + +static GType *object_types = NULL; + +static GString *xmlstr = NULL; + +static const gchar* +xmlprint (gint indent, const gchar *tag, const gchar *data) +{ + const gchar indent_str[] = " "; + + /* reset */ + g_string_truncate (xmlstr, 0); + g_string_append_len (xmlstr, indent_str, MIN (indent, strlen (indent_str))); + g_string_append_printf (xmlstr, "<%s>", tag); + + if (data) { + gchar *s; + + s = g_markup_escape_text (data, -1); + g_string_append (xmlstr, s); + g_free (s); + } + + g_string_append_printf (xmlstr, "\\n", tag); + return xmlstr->str; +} + +static gint +gst_feature_sort_compare (gconstpointer a, gconstpointer b) +{ + const gchar *name_a = gst_plugin_feature_get_name ((GstPluginFeature *) a); + const gchar *name_b = gst_plugin_feature_get_name ((GstPluginFeature *) b); + return strcmp (name_a, name_b); +} + +static gint +static_pad_template_compare (gconstpointer a, gconstpointer b) +{ + GstStaticPadTemplate *spt_a = (GstStaticPadTemplate *) a; + GstStaticPadTemplate *spt_b = (GstStaticPadTemplate *) b; + + /* we want SINK before SRC (enum is UNKNOWN, SRC, SINK) */ + if (spt_a->direction != spt_b->direction) + return spt_b->direction - spt_a->direction; + + /* we want ALWAYS first, SOMETIMES second, REQUEST last + * (enum is ALWAYS, SOMETIMES, REQUEST) */ + if (spt_a->presence != spt_b->presence) + return spt_a->presence - spt_b->presence; + + return strcmp (spt_a->name_template, spt_b->name_template); +} + +static GType * +get_object_types (void) +{ + gpointer g_object_class; + GList *plugins = NULL; + GList *factories = NULL; + GList *l; + GstElementFactory *factory = NULL; + GType type; + gint i = 0; + gboolean reinspect; + + /* get a list of features from plugins in our source module */ + plugins = gst_registry_get_plugin_list (gst_registry_get ()); + + xmlstr = g_string_new (""); + + reinspect = !g_file_test ("scanobj-build.stamp", G_FILE_TEST_EXISTS); + + while (plugins) { + GList *features; + GstPlugin *plugin; + const gchar *source; + FILE *inspect = NULL; + gchar *inspect_name; + + plugin = (GstPlugin *) (plugins->data); + plugins = g_list_next (plugins); + source = gst_plugin_get_source (plugin); + if (!source || strcmp (source, "$SOURCE") != 0) { + continue; + } + + /* skip static coreelements plugin with pipeline and bin element factory */ + if (gst_plugin_get_filename (plugin) == NULL) + continue; + + $debug_log ("plugin: %s source: %s", gst_plugin_get_name (plugin), source); + + if (reinspect) { + gchar *basename; + + inspect_name = g_strdup_printf ("$INSPECT_DIR" G_DIR_SEPARATOR_S "plugin-%s.xml", + gst_plugin_get_name (plugin)); + inspect = fopen (inspect_name, "w"); + if (inspect == NULL) { + g_error ("Could not open %s for writing: %s\\n", inspect_name, + g_strerror (errno)); + } + g_free (inspect_name); + + basename = g_path_get_basename (gst_plugin_get_filename (plugin)); + + /* output plugin data */ + fputs ("\\n",inspect); + fputs (xmlprint(2, "name", gst_plugin_get_name (plugin)),inspect); + fputs (xmlprint(2, "description", gst_plugin_get_description (plugin)),inspect); + fputs (xmlprint(2, "filename", gst_plugin_get_filename (plugin)),inspect); + fputs (xmlprint(2, "basename", basename),inspect); + fputs (xmlprint(2, "version", gst_plugin_get_version (plugin)),inspect); + fputs (xmlprint(2, "license", gst_plugin_get_license (plugin)),inspect); + fputs (xmlprint(2, "source", gst_plugin_get_source (plugin)),inspect); + fputs (xmlprint(2, "package", gst_plugin_get_package (plugin)),inspect); + fputs (xmlprint(2, "origin", gst_plugin_get_origin (plugin)),inspect); + fputs (" \\n", inspect); + + g_free (basename); + } + + features = + gst_registry_get_feature_list_by_plugin (gst_registry_get (), + gst_plugin_get_name (plugin)); + + /* sort factories by feature->name */ + features = g_list_sort (features, gst_feature_sort_compare); + + while (features) { + GstPluginFeature *feature; + feature = GST_PLUGIN_FEATURE (features->data); + feature = gst_plugin_feature_load (feature); + if (!feature) { + g_warning ("Could not load plugin feature %s", + gst_plugin_feature_get_name (feature)); + } + + if (GST_IS_ELEMENT_FACTORY (feature)) { + const gchar *pad_dir[] = { "unknown","source","sink" }; + const gchar *pad_pres[] = { "always","sometimes","request" }; + GList *pads, *pad; + + $debug_log (" feature: %s", gst_plugin_feature_get_name (feature)); + + factory = GST_ELEMENT_FACTORY (feature); + factories = g_list_prepend (factories, factory); + + if (reinspect) { + /* output element data */ + fputs (" \\n", inspect); + fputs (xmlprint(6, "name", gst_plugin_feature_get_name (feature)),inspect); + fputs (xmlprint(6, "longname", gst_element_factory_get_metadata (factory, GST_ELEMENT_METADATA_LONGNAME)),inspect); + fputs (xmlprint(6, "class", gst_element_factory_get_metadata (factory, GST_ELEMENT_METADATA_KLASS)),inspect); + fputs (xmlprint(6, "description", gst_element_factory_get_metadata (factory, GST_ELEMENT_METADATA_DESCRIPTION)),inspect); + fputs (xmlprint(6, "author", gst_element_factory_get_metadata (factory, GST_ELEMENT_METADATA_AUTHOR)),inspect); + fputs (" \\n", inspect); + + /* output pad-template data */ + pads = g_list_copy ((GList *) gst_element_factory_get_static_pad_templates (factory)); + pads = g_list_sort (pads, static_pad_template_compare); + for (pad = pads; pad != NULL; pad = pad->next) { + GstStaticPadTemplate *pt = pad->data; + + fputs (" \\n", inspect); + fputs (xmlprint(10, "name", pt->name_template),inspect); + fputs (xmlprint(10, "direction", pad_dir[pt->direction]),inspect); + fputs (xmlprint(10, "presence", pad_pres[pt->presence]),inspect); + fputs (xmlprint(10, "details", pt->static_caps.string),inspect); + fputs (" \\n", inspect); + } + g_list_free (pads); + fputs (" \\n \\n", inspect); + } + } + features = g_list_next (features); + } + + if (reinspect) { + fputs (" \\n", inspect); + fclose (inspect); + } + } + + g_string_free (xmlstr, TRUE); + + $debug_log ("number of element factories: %d", g_list_length (factories)); + + /* allocate the object_types array to hold them */ + object_types = g_new0 (GType, g_list_length (factories)+$ntypes+1); + + l = factories; + i = 0; + + /* fill it */ + while (l) { + factory = GST_ELEMENT_FACTORY (l->data); + type = gst_element_factory_get_element_type (factory); + if (type != 0) { + $debug_log ("adding type for factory %s", gst_element_factory_get_metadata (factory, GST_ELEMENT_METADATA_LONGNAME)); + object_types[i++] = type; + } else { + g_message ("type info for factory %s not found", + gst_element_factory_get_metadata (factory, GST_ELEMENT_METADATA_LONGNAME)); + } + l = g_list_next (l); + } + +EOT + +# get_type functions: +for (@types) { +print OUTPUT < uppercase with '_' + * GFileMonitor -> file_monitor + * GIOExtensionPoint -> extension_point + * GtkTreeView -> tree_view + * if 2nd char is upper case too + * search for first lower case and go back one char + * else + * search for next upper case + */ + if (!strncmp (object_name, "Gtk", 3)) + object_arg = object_name + 3; + else if (!strncmp (object_name, "Gnome", 5)) + object_arg = object_name + 5; + else + object_arg = object_name; + + object_arg_lower = g_ascii_strdown (object_arg, -1); + sprintf (pos, "*%s\\n", object_arg_lower); + pos += strlen (pos); + if (!strncmp (object_arg_lower, "widget", 6)) + widget_num = 2; + g_free(object_arg_lower); + + /* Convert signal name to use underscores rather than dashes '-'. */ + strncpy (signal_name, query_info.signal_name, 127); + signal_name[127] = '\\0'; + for (i = 0; signal_name[i]; i++) + { + if (signal_name[i] == '-') + signal_name[i] = '_'; + } + + /* Output the signal parameters. */ + for (param = 0; param < query_info.n_params; param++) + { + type_name = get_type_name (query_info.param_types[param] & ~G_SIGNAL_TYPE_STATIC_SCOPE, &is_pointer); + + /* Most arguments to the callback are called "arg1", "arg2", etc. + GtkWidgets are called "widget", "widget2", ... + GtkCallbacks are called "callback", "callback2", ... */ + if (!strcmp (type_name, "GtkWidget")) + { + arg_name = "widget"; + arg_num = &widget_num; + } + else if (!strcmp (type_name, "GtkCallback") + || !strcmp (type_name, "GtkCCallback")) + { + arg_name = "callback"; + arg_num = &callback_num; + } + else + { + arg_name = "arg"; + arg_num = ¶m_num; + } + sprintf (pos, "%s ", type_name); + pos += strlen (pos); + + if (!arg_num || *arg_num == 0) + sprintf (pos, "%s%s\\n", is_pointer ? "*" : " ", arg_name); + else + sprintf (pos, "%s%s%i\\n", is_pointer ? "*" : " ", arg_name, + *arg_num); + pos += strlen (pos); + + if (arg_num) + { + if (*arg_num == 0) + *arg_num = 2; + else + *arg_num += 1; + } + } + + pos = flags; + /* We use one-character flags for simplicity. */ + if (query_info.signal_flags & G_SIGNAL_RUN_FIRST) + *pos++ = 'f'; + if (query_info.signal_flags & G_SIGNAL_RUN_LAST) + *pos++ = 'l'; + if (query_info.signal_flags & G_SIGNAL_RUN_CLEANUP) + *pos++ = 'c'; + if (query_info.signal_flags & G_SIGNAL_NO_RECURSE) + *pos++ = 'r'; + if (query_info.signal_flags & G_SIGNAL_DETAILED) + *pos++ = 'd'; + if (query_info.signal_flags & G_SIGNAL_ACTION) + *pos++ = 'a'; + if (query_info.signal_flags & G_SIGNAL_NO_HOOKS) + *pos++ = 'h'; + *pos = 0; + + /* Output the return type and function name. */ + ret_type = get_type_name (query_info.return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE, &is_pointer); + + fprintf (fp, + "\\n%s::%s\\n%s%s\\n%s\\n%s\\n\\n", + object_name, query_info.signal_name, ret_type, is_pointer ? "*" : "", flags, buffer); +} + + +/* Returns the type name to use for a signal argument or return value, given + the GtkType from the signal info. It also sets is_pointer to TRUE if the + argument needs a '*' since it is a pointer. */ +static const gchar * +get_type_name (GType type, gboolean * is_pointer) +{ + const gchar *type_name; + + *is_pointer = FALSE; + type_name = g_type_name (type); + + switch (type) { + case G_TYPE_NONE: + case G_TYPE_CHAR: + case G_TYPE_UCHAR: + case G_TYPE_BOOLEAN: + case G_TYPE_INT: + case G_TYPE_UINT: + case G_TYPE_LONG: + case G_TYPE_ULONG: + case G_TYPE_FLOAT: + case G_TYPE_DOUBLE: + case G_TYPE_POINTER: + /* These all have normal C type names so they are OK. */ + return type_name; + + case G_TYPE_STRING: + /* A GtkString is really a gchar*. */ + *is_pointer = TRUE; + return "gchar"; + + case G_TYPE_ENUM: + case G_TYPE_FLAGS: + /* We use a gint for both of these. Hopefully a subtype with a decent + name will be registered and used instead, as GTK+ does itself. */ + return "gint"; + + case G_TYPE_BOXED: + /* The boxed type shouldn't be used itself, only subtypes. Though we + return 'gpointer' just in case. */ + return "gpointer"; + + case G_TYPE_PARAM: + /* A GParam is really a GParamSpec*. */ + *is_pointer = TRUE; + return "GParamSpec"; + +#if GLIB_CHECK_VERSION (2, 25, 9) + case G_TYPE_VARIANT: + *is_pointer = TRUE; + return "GVariant"; +#endif + +default: + break; + } + + /* For all GObject subclasses we can use the class name with a "*", + e.g. 'GtkWidget *'. */ + if (g_type_is_a (type, G_TYPE_OBJECT)) + *is_pointer = TRUE; + + /* Also catch non GObject root types */ + if (G_TYPE_IS_CLASSED (type)) + *is_pointer = TRUE; + + /* All boxed subtypes will be pointers as well. */ + /* Exception: GStrv */ + if (g_type_is_a (type, G_TYPE_BOXED) && + !g_type_is_a (type, G_TYPE_STRV)) + *is_pointer = TRUE; + + /* All pointer subtypes will be pointers as well. */ + if (g_type_is_a (type, G_TYPE_POINTER)) + *is_pointer = TRUE; + + /* But enums are not */ + if (g_type_is_a (type, G_TYPE_ENUM) || + g_type_is_a (type, G_TYPE_FLAGS)) + *is_pointer = FALSE; + + return type_name; +} + + +/* This outputs the hierarchy of all objects which have been initialized, + i.e. by calling their XXX_get_type() initialization function. */ +static void +output_object_hierarchy (void) +{ + FILE *fp; + gint i,j; + GType root, type; + GType root_types[$ntypes] = { G_TYPE_INVALID, }; + + fp = fopen (hierarchy_filename, "w"); + if (fp == NULL) + { + g_warning ("Couldn't open output file: %s : %s", hierarchy_filename, g_strerror(errno)); + return; + } + output_hierarchy (fp, G_TYPE_OBJECT, 0); + output_hierarchy (fp, G_TYPE_INTERFACE, 0); + + for (i=0; object_types[i]; i++) { + root = object_types[i]; + while ((type = g_type_parent (root))) { + root = type; + } + if ((root != G_TYPE_OBJECT) && (root != G_TYPE_INTERFACE)) { + for (j=0; root_types[j]; j++) { + if (root == root_types[j]) { + root = G_TYPE_INVALID; break; + } + } + if(root) { + root_types[j] = root; + output_hierarchy (fp, root, 0); + } + } + } + + fclose (fp); +} + +static int +compare_types (const void *a, const void *b) +{ + const char *na = g_type_name (*((GType *)a)); + const char *nb = g_type_name (*((GType *)b)); + + return g_strcmp0 (na, nb); +} + + +/* This is called recursively to output the hierarchy of a object. */ +static void +output_hierarchy (FILE *fp, + GType type, + guint level) +{ + guint i; + GType *children; + guint n_children; + + if (!type) + return; + + for (i = 0; i < level; i++) + fprintf (fp, " "); + fprintf (fp, "%s\\n", g_type_name (type)); + + children = g_type_children (type, &n_children); + qsort (children, n_children, sizeof (GType), compare_types); + + + for (i=0; i < n_children; i++) + output_hierarchy (fp, children[i], level + 1); + + g_free (children); +} + +static void output_object_interfaces (void) +{ + guint i; + FILE *fp; + + fp = fopen (interfaces_filename, "w"); + if (fp == NULL) + { + g_warning ("Couldn't open output file: %s : %s", interfaces_filename, g_strerror(errno)); + return; + } + output_interfaces (fp, G_TYPE_OBJECT); + + for (i = 0; object_types[i]; i++) + { + if (!g_type_parent (object_types[i]) && + (object_types[i] != G_TYPE_OBJECT) && + G_TYPE_IS_INSTANTIATABLE (object_types[i])) + { + output_interfaces (fp, object_types[i]); + } + } + fclose (fp); +} + +static void +output_interfaces (FILE *fp, + GType type) +{ + guint i; + GType *children, *interfaces; + guint n_children, n_interfaces; + + if (!type) + return; + + interfaces = g_type_interfaces (type, &n_interfaces); + + if (n_interfaces > 0) + { + fprintf (fp, "%s", g_type_name (type)); + for (i=0; i < n_interfaces; i++) + fprintf (fp, " %s", g_type_name (interfaces[i])); + fprintf (fp, "\\n"); + } + g_free (interfaces); + + children = g_type_children (type, &n_children); + + for (i=0; i < n_children; i++) + output_interfaces (fp, children[i]); + + g_free (children); +} + +static void output_interface_prerequisites (void) +{ + FILE *fp; + + fp = fopen (prerequisites_filename, "w"); + if (fp == NULL) + { + g_warning ("Couldn't open output file: %s : %s", prerequisites_filename, g_strerror(errno)); + return; + } + output_prerequisites (fp, G_TYPE_INTERFACE); + fclose (fp); +} + +static void +output_prerequisites (FILE *fp, + GType type) +{ +#if GLIB_CHECK_VERSION(2,1,0) + guint i; + GType *children, *prerequisites; + guint n_children, n_prerequisites; + + if (!type) + return; + + prerequisites = g_type_interface_prerequisites (type, &n_prerequisites); + + if (n_prerequisites > 0) + { + fprintf (fp, "%s", g_type_name (type)); + for (i=0; i < n_prerequisites; i++) + fprintf (fp, " %s", g_type_name (prerequisites[i])); + fprintf (fp, "\\n"); + } + g_free (prerequisites); + + children = g_type_children (type, &n_children); + + for (i=0; i < n_children; i++) + output_prerequisites (fp, children[i]); + + g_free (children); +#endif +} + +static void +output_args (void) +{ + FILE *fp; + gint i; + + fp = fopen (args_filename, "w"); + if (fp == NULL) + { + g_warning ("Couldn't open output file: %s : %s", args_filename, g_strerror(errno)); + return; + } + + for (i = 0; object_types[i]; i++) { + output_object_args (fp, object_types[i]); + } + + fclose (fp); +} + +static gint +compare_param_specs (const void *a, const void *b) +{ + GParamSpec *spec_a = *(GParamSpec **)a; + GParamSpec *spec_b = *(GParamSpec **)b; + + return strcmp (g_param_spec_get_name (spec_a), g_param_spec_get_name (spec_b)); +} + +/* Its common to have unsigned properties restricted + * to the signed range. Therefore we make this look + * a bit nicer by spelling out the max constants. + */ + +/* Don't use "==" with floats, it might trigger a gcc warning. */ +#define GTKDOC_COMPARE_FLOAT(x, y) (x <= y && x >= y) + +static gchar* +describe_double_constant (gdouble value) +{ + gchar *desc; + + if (GTKDOC_COMPARE_FLOAT (value, G_MAXDOUBLE)) + desc = g_strdup ("G_MAXDOUBLE"); + else if (GTKDOC_COMPARE_FLOAT (value, G_MINDOUBLE)) + desc = g_strdup ("G_MINDOUBLE"); + else if (GTKDOC_COMPARE_FLOAT (value, -G_MAXDOUBLE)) + desc = g_strdup ("-G_MAXDOUBLE"); + else if (GTKDOC_COMPARE_FLOAT (value, G_MAXFLOAT)) + desc = g_strdup ("G_MAXFLOAT"); + else if (GTKDOC_COMPARE_FLOAT (value, G_MINFLOAT)) + desc = g_strdup ("G_MINFLOAT"); + else if (GTKDOC_COMPARE_FLOAT (value, -G_MAXFLOAT)) + desc = g_strdup ("-G_MAXFLOAT"); + else{ + /* make sure floats are output with a decimal dot irrespective of + * current locale. Use formatd since we want human-readable numbers + * and do not need the exact same bit representation when deserialising */ + desc = g_malloc0 (G_ASCII_DTOSTR_BUF_SIZE); + g_ascii_formatd (desc, G_ASCII_DTOSTR_BUF_SIZE, "%g", value); + } + + return desc; +} + +static gchar* +describe_signed_constant (gsize size, gint64 value) +{ + gchar *desc = NULL; + + switch (size) { + case 8: + if (value == G_MAXINT64) + desc = g_strdup ("G_MAXINT64"); + else if (value == G_MININT64) + desc = g_strdup ("G_MININT64"); + /* fall through */ + case 4: + if (sizeof (int) == 4) { + if (value == G_MAXINT) + desc = g_strdup ("G_MAXINT"); + else if (value == G_MININT) + desc = g_strdup ("G_MININT"); + else if (value == (gint64)G_MAXUINT) + desc = g_strdup ("G_MAXUINT"); + } + if (value == G_MAXLONG) + desc = g_strdup ("G_MAXLONG"); + else if (value == G_MINLONG) + desc = g_strdup ("G_MINLONG"); + else if (value == (gint64)G_MAXULONG) + desc = g_strdup ("G_MAXULONG"); + /* fall through */ + case 2: + if (sizeof (int) == 2) { + if (value == G_MAXINT) + desc = g_strdup ("G_MAXINT"); + else if (value == G_MININT) + desc = g_strdup ("G_MININT"); + else if (value == (gint64)G_MAXUINT) + desc = g_strdup ("G_MAXUINT"); + } + break; + default: + break; + } + if (!desc) + desc = g_strdup_printf ("%" G_GINT64_FORMAT, value); + + return desc; +} + +static gchar* +describe_unsigned_constant (gsize size, guint64 value) +{ + gchar *desc = NULL; + + switch (size) { + case 8: + if (value == G_MAXINT64) + desc = g_strdup ("G_MAXINT64"); + else if (value == G_MAXUINT64) + desc = g_strdup ("G_MAXUINT64"); + /* fall through */ + case 4: + if (sizeof (int) == 4) { + if (value == (guint64)G_MAXINT) + desc = g_strdup ("G_MAXINT"); + else if (value == G_MAXUINT) + desc = g_strdup ("G_MAXUINT"); + } + if (value == (guint64)G_MAXLONG) + desc = g_strdup ("G_MAXLONG"); + else if (value == G_MAXULONG) + desc = g_strdup ("G_MAXULONG"); + /* fall through */ + case 2: + if (sizeof (int) == 2) { + if (value == (guint64)G_MAXINT) + desc = g_strdup ("G_MAXINT"); + else if (value == G_MAXUINT) + desc = g_strdup ("G_MAXUINT"); + } + break; + default: + break; + } + if (!desc) + desc = g_strdup_printf ("%" G_GUINT64_FORMAT, value); + + return desc; +} + +static gchar* +describe_type (GParamSpec *spec) +{ + gchar *desc; + gchar *lower; + gchar *upper; + + if (G_IS_PARAM_SPEC_CHAR (spec)) + { + GParamSpecChar *pspec = G_PARAM_SPEC_CHAR (spec); + + lower = describe_signed_constant (sizeof(gchar), pspec->minimum); + upper = describe_signed_constant (sizeof(gchar), pspec->maximum); + if (pspec->minimum == G_MININT8 && pspec->maximum == G_MAXINT8) + desc = g_strdup (""); + else if (pspec->minimum == G_MININT8) + desc = g_strdup_printf ("<= %s", upper); + else if (pspec->maximum == G_MAXINT8) + desc = g_strdup_printf (">= %s", lower); + else + desc = g_strdup_printf ("[%s,%s]", lower, upper); + g_free (lower); + g_free (upper); + } + else if (G_IS_PARAM_SPEC_UCHAR (spec)) + { + GParamSpecUChar *pspec = G_PARAM_SPEC_UCHAR (spec); + + lower = describe_unsigned_constant (sizeof(guchar), pspec->minimum); + upper = describe_unsigned_constant (sizeof(guchar), pspec->maximum); + if (pspec->minimum == 0 && pspec->maximum == G_MAXUINT8) + desc = g_strdup (""); + else if (pspec->minimum == 0) + desc = g_strdup_printf ("<= %s", upper); + else if (pspec->maximum == G_MAXUINT8) + desc = g_strdup_printf (">= %s", lower); + else + desc = g_strdup_printf ("[%s,%s]", lower, upper); + g_free (lower); + g_free (upper); + } + else if (G_IS_PARAM_SPEC_INT (spec)) + { + GParamSpecInt *pspec = G_PARAM_SPEC_INT (spec); + + lower = describe_signed_constant (sizeof(gint), pspec->minimum); + upper = describe_signed_constant (sizeof(gint), pspec->maximum); + if (pspec->minimum == G_MININT && pspec->maximum == G_MAXINT) + desc = g_strdup (""); + else if (pspec->minimum == G_MININT) + desc = g_strdup_printf ("<= %s", upper); + else if (pspec->maximum == G_MAXINT) + desc = g_strdup_printf (">= %s", lower); + else + desc = g_strdup_printf ("[%s,%s]", lower, upper); + g_free (lower); + g_free (upper); + } + else if (G_IS_PARAM_SPEC_UINT (spec)) + { + GParamSpecUInt *pspec = G_PARAM_SPEC_UINT (spec); + + lower = describe_unsigned_constant (sizeof(guint), pspec->minimum); + upper = describe_unsigned_constant (sizeof(guint), pspec->maximum); + if (pspec->minimum == 0 && pspec->maximum == G_MAXUINT) + desc = g_strdup (""); + else if (pspec->minimum == 0) + desc = g_strdup_printf ("<= %s", upper); + else if (pspec->maximum == G_MAXUINT) + desc = g_strdup_printf (">= %s", lower); + else + desc = g_strdup_printf ("[%s,%s]", lower, upper); + g_free (lower); + g_free (upper); + } + else if (G_IS_PARAM_SPEC_LONG (spec)) + { + GParamSpecLong *pspec = G_PARAM_SPEC_LONG (spec); + + lower = describe_signed_constant (sizeof(glong), pspec->minimum); + upper = describe_signed_constant (sizeof(glong), pspec->maximum); + if (pspec->minimum == G_MINLONG && pspec->maximum == G_MAXLONG) + desc = g_strdup (""); + else if (pspec->minimum == G_MINLONG) + desc = g_strdup_printf ("<= %s", upper); + else if (pspec->maximum == G_MAXLONG) + desc = g_strdup_printf (">= %s", lower); + else + desc = g_strdup_printf ("[%s,%s]", lower, upper); + g_free (lower); + g_free (upper); + } + else if (G_IS_PARAM_SPEC_ULONG (spec)) + { + GParamSpecULong *pspec = G_PARAM_SPEC_ULONG (spec); + + lower = describe_unsigned_constant (sizeof(gulong), pspec->minimum); + upper = describe_unsigned_constant (sizeof(gulong), pspec->maximum); + if (pspec->minimum == 0 && pspec->maximum == G_MAXULONG) + desc = g_strdup (""); + else if (pspec->minimum == 0) + desc = g_strdup_printf ("<= %s", upper); + else if (pspec->maximum == G_MAXULONG) + desc = g_strdup_printf (">= %s", lower); + else + desc = g_strdup_printf ("[%s,%s]", lower, upper); + g_free (lower); + g_free (upper); + } + else if (G_IS_PARAM_SPEC_INT64 (spec)) + { + GParamSpecInt64 *pspec = G_PARAM_SPEC_INT64 (spec); + + lower = describe_signed_constant (sizeof(gint64), pspec->minimum); + upper = describe_signed_constant (sizeof(gint64), pspec->maximum); + if (pspec->minimum == G_MININT64 && pspec->maximum == G_MAXINT64) + desc = g_strdup (""); + else if (pspec->minimum == G_MININT64) + desc = g_strdup_printf ("<= %s", upper); + else if (pspec->maximum == G_MAXINT64) + desc = g_strdup_printf (">= %s", lower); + else + desc = g_strdup_printf ("[%s,%s]", lower, upper); + g_free (lower); + g_free (upper); + } + else if (G_IS_PARAM_SPEC_UINT64 (spec)) + { + GParamSpecUInt64 *pspec = G_PARAM_SPEC_UINT64 (spec); + + lower = describe_unsigned_constant (sizeof(guint64), pspec->minimum); + upper = describe_unsigned_constant (sizeof(guint64), pspec->maximum); + if (pspec->minimum == 0 && pspec->maximum == G_MAXUINT64) + desc = g_strdup (""); + else if (pspec->minimum == 0) + desc = g_strdup_printf ("<= %s", upper); + else if (pspec->maximum == G_MAXUINT64) + desc = g_strdup_printf (">= %s", lower); + else + desc = g_strdup_printf ("[%s,%s]", lower, upper); + g_free (lower); + g_free (upper); + } + else if (G_IS_PARAM_SPEC_FLOAT (spec)) + { + GParamSpecFloat *pspec = G_PARAM_SPEC_FLOAT (spec); + + lower = describe_double_constant (pspec->minimum); + upper = describe_double_constant (pspec->maximum); + if (GTKDOC_COMPARE_FLOAT (pspec->minimum, -G_MAXFLOAT)) + { + if (GTKDOC_COMPARE_FLOAT (pspec->maximum, G_MAXFLOAT)) + desc = g_strdup (""); + else + desc = g_strdup_printf ("<= %s", upper); + } + else if (GTKDOC_COMPARE_FLOAT (pspec->maximum, G_MAXFLOAT)) + desc = g_strdup_printf (">= %s", lower); + else + desc = g_strdup_printf ("[%s,%s]", lower, upper); + g_free (lower); + g_free (upper); + } + else if (G_IS_PARAM_SPEC_DOUBLE (spec)) + { + GParamSpecDouble *pspec = G_PARAM_SPEC_DOUBLE (spec); + + lower = describe_double_constant (pspec->minimum); + upper = describe_double_constant (pspec->maximum); + if (GTKDOC_COMPARE_FLOAT (pspec->minimum, -G_MAXDOUBLE)) + { + if (GTKDOC_COMPARE_FLOAT (pspec->maximum, G_MAXDOUBLE)) + desc = g_strdup (""); + else + desc = g_strdup_printf ("<= %s", upper); + } + else if (GTKDOC_COMPARE_FLOAT (pspec->maximum, G_MAXDOUBLE)) + desc = g_strdup_printf (">= %s", lower); + else + desc = g_strdup_printf ("[%s,%s]", lower, upper); + g_free (lower); + g_free (upper); + } +#if GLIB_CHECK_VERSION (2, 12, 0) + else if (G_IS_PARAM_SPEC_GTYPE (spec)) + { + GParamSpecGType *pspec = G_PARAM_SPEC_GTYPE (spec); + gboolean is_pointer; + + desc = g_strdup (get_type_name (pspec->is_a_type, &is_pointer)); + } +#endif +#if GLIB_CHECK_VERSION (2, 25, 9) + else if (G_IS_PARAM_SPEC_VARIANT (spec)) + { + GParamSpecVariant *pspec = G_PARAM_SPEC_VARIANT (spec); + gchar *variant_type; + + variant_type = g_variant_type_dup_string (pspec->type); + desc = g_strdup_printf ("GVariant<%s>", variant_type); + g_free (variant_type); + } +#endif + else + { + desc = g_strdup (""); + } + + return desc; +} + +static gchar* +describe_default (GParamSpec *spec) +{ + gchar *desc; + + if (G_IS_PARAM_SPEC_CHAR (spec)) + { + GParamSpecChar *pspec = G_PARAM_SPEC_CHAR (spec); + + desc = g_strdup_printf ("%d", pspec->default_value); + } + else if (G_IS_PARAM_SPEC_UCHAR (spec)) + { + GParamSpecUChar *pspec = G_PARAM_SPEC_UCHAR (spec); + + desc = g_strdup_printf ("%u", pspec->default_value); + } + else if (G_IS_PARAM_SPEC_BOOLEAN (spec)) + { + GParamSpecBoolean *pspec = G_PARAM_SPEC_BOOLEAN (spec); + + desc = g_strdup_printf ("%s", pspec->default_value ? "TRUE" : "FALSE"); + } + else if (G_IS_PARAM_SPEC_INT (spec)) + { + GParamSpecInt *pspec = G_PARAM_SPEC_INT (spec); + + desc = g_strdup_printf ("%d", pspec->default_value); + } + else if (G_IS_PARAM_SPEC_UINT (spec)) + { + GParamSpecUInt *pspec = G_PARAM_SPEC_UINT (spec); + + desc = g_strdup_printf ("%u", pspec->default_value); + } + else if (G_IS_PARAM_SPEC_LONG (spec)) + { + GParamSpecLong *pspec = G_PARAM_SPEC_LONG (spec); + + desc = g_strdup_printf ("%ld", pspec->default_value); + } + else if (G_IS_PARAM_SPEC_LONG (spec)) + { + GParamSpecULong *pspec = G_PARAM_SPEC_ULONG (spec); + + desc = g_strdup_printf ("%lu", pspec->default_value); + } + else if (G_IS_PARAM_SPEC_INT64 (spec)) + { + GParamSpecInt64 *pspec = G_PARAM_SPEC_INT64 (spec); + + desc = g_strdup_printf ("%" G_GINT64_FORMAT, pspec->default_value); + } + else if (G_IS_PARAM_SPEC_UINT64 (spec)) + { + GParamSpecUInt64 *pspec = G_PARAM_SPEC_UINT64 (spec); + + desc = g_strdup_printf ("%" G_GUINT64_FORMAT, pspec->default_value); + } + else if (G_IS_PARAM_SPEC_UNICHAR (spec)) + { + GParamSpecUnichar *pspec = G_PARAM_SPEC_UNICHAR (spec); + + if (g_unichar_isprint (pspec->default_value)) + desc = g_strdup_printf ("'%c'", pspec->default_value); + else + desc = g_strdup_printf ("%u", pspec->default_value); + } + else if (G_IS_PARAM_SPEC_ENUM (spec)) + { + GParamSpecEnum *pspec = G_PARAM_SPEC_ENUM (spec); + + GEnumValue *value = g_enum_get_value (pspec->enum_class, pspec->default_value); + if (value) + desc = g_strdup_printf ("%s", value->value_name); + else + desc = g_strdup_printf ("%d", pspec->default_value); + } + else if (G_IS_PARAM_SPEC_FLAGS (spec)) + { + GParamSpecFlags *pspec = G_PARAM_SPEC_FLAGS (spec); + guint default_value; + GString *acc; + + default_value = pspec->default_value; + acc = g_string_new (""); + + while (default_value) + { + GFlagsValue *value = g_flags_get_first_value (pspec->flags_class, default_value); + + if (!value) + break; + + if (acc->len > 0) + g_string_append (acc, "|"); + g_string_append (acc, value->value_name); + + default_value &= ~value->value; + } + + if (default_value == 0) + desc = g_string_free (acc, FALSE); + else + { + desc = g_strdup_printf ("%d", pspec->default_value); + g_string_free (acc, TRUE); + } + } + else if (G_IS_PARAM_SPEC_FLOAT (spec)) + { + GParamSpecFloat *pspec = G_PARAM_SPEC_FLOAT (spec); + + /* make sure floats are output with a decimal dot irrespective of + * current locale. Use formatd since we want human-readable numbers + * and do not need the exact same bit representation when deserialising */ + desc = g_malloc0 (G_ASCII_DTOSTR_BUF_SIZE); + g_ascii_formatd (desc, G_ASCII_DTOSTR_BUF_SIZE, "%g", + pspec->default_value); + } + else if (G_IS_PARAM_SPEC_DOUBLE (spec)) + { + GParamSpecDouble *pspec = G_PARAM_SPEC_DOUBLE (spec); + + /* make sure floats are output with a decimal dot irrespective of + * current locale. Use formatd since we want human-readable numbers + * and do not need the exact same bit representation when deserialising */ + desc = g_malloc0 (G_ASCII_DTOSTR_BUF_SIZE); + g_ascii_formatd (desc, G_ASCII_DTOSTR_BUF_SIZE, "%g", + pspec->default_value); + } + else if (G_IS_PARAM_SPEC_STRING (spec)) + { + GParamSpecString *pspec = G_PARAM_SPEC_STRING (spec); + + if (pspec->default_value) + { + gchar *esc = g_strescape (pspec->default_value, NULL); + + desc = g_strdup_printf ("\\"%s\\"", esc); + + g_free (esc); + } + else + desc = g_strdup_printf ("NULL"); + } + else + { + desc = g_strdup (""); + } + + return desc; +} + + +static void +output_object_args (FILE *fp, GType object_type) +{ + gpointer class; + const gchar *object_class_name; + guint arg; + gchar flags[16], *pos; + GParamSpec **properties; + guint n_properties; + gboolean child_prop; + gboolean style_prop; + gboolean is_pointer; + const gchar *type_name; + gchar *type_desc; + gchar *default_value; + + if (G_TYPE_IS_OBJECT (object_type)) + { + class = g_type_class_peek (object_type); + if (!class) + return; + + properties = g_object_class_list_properties (class, &n_properties); + } +#if GLIB_MAJOR_VERSION > 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 3) + else if (G_TYPE_IS_INTERFACE (object_type)) + { + class = g_type_default_interface_ref (object_type); + + if (!class) + return; + + properties = g_object_interface_list_properties (class, &n_properties); + } +#endif + else + return; + + object_class_name = g_type_name (object_type); + + child_prop = FALSE; + style_prop = FALSE; + + while (TRUE) { + qsort (properties, n_properties, sizeof (GParamSpec *), compare_param_specs); + for (arg = 0; arg < n_properties; arg++) + { + GParamSpec *spec = properties[arg]; + const gchar *nick, *blurb, *dot; + + if (spec->owner_type != object_type) + continue; + + pos = flags; + /* We use one-character flags for simplicity. */ + if (child_prop && !style_prop) + *pos++ = 'c'; + if (style_prop) + *pos++ = 's'; + if (spec->flags & G_PARAM_READABLE) + *pos++ = 'r'; + if (spec->flags & G_PARAM_WRITABLE) + *pos++ = 'w'; + if (spec->flags & G_PARAM_CONSTRUCT) + *pos++ = 'x'; + if (spec->flags & G_PARAM_CONSTRUCT_ONLY) + *pos++ = 'X'; + *pos = 0; + + nick = g_param_spec_get_nick (spec); + blurb = g_param_spec_get_blurb (spec); + + dot = ""; + if (blurb) { + int str_len = strlen (blurb); + if (str_len > 0 && blurb[str_len - 1] != '.') + dot = "."; + } + + type_desc = describe_type (spec); + default_value = describe_default (spec); + type_name = get_type_name (spec->value_type, &is_pointer); + fprintf (fp, "\\n%s::%s\\n%s%s\\n%s\\n%s\\n%s\\n%s%s\\n%s\\n\\n\\n", + object_class_name, g_param_spec_get_name (spec), type_name, is_pointer ? "*" : "", type_desc, flags, nick ? nick : "(null)", blurb ? blurb : "(null)", dot, default_value); + g_free (type_desc); + g_free (default_value); + } + + g_free (properties); + +#ifdef GTK_IS_CONTAINER_CLASS + if (!child_prop && GTK_IS_CONTAINER_CLASS (class)) { + properties = gtk_container_class_list_child_properties (class, &n_properties); + child_prop = TRUE; + continue; + } +#endif + +#ifdef GTK_IS_CELL_AREA_CLASS + if (!child_prop && GTK_IS_CELL_AREA_CLASS (class)) { + properties = gtk_cell_area_class_list_cell_properties (class, &n_properties); + child_prop = TRUE; + continue; + } +#endif + +#ifdef GTK_IS_WIDGET_CLASS +#if GTK_CHECK_VERSION(2,1,0) + if (!style_prop && GTK_IS_WIDGET_CLASS (class)) { + properties = gtk_widget_class_list_style_properties (GTK_WIDGET_CLASS (class), &n_properties); + style_prop = TRUE; + continue; + } +#endif +#endif + + break; + } +} +EOT + +close OUTPUT; + +# Compile and run our file + +$CC = $ENV{CC} ? $ENV{CC} : "gcc"; +$LD = $ENV{LD} ? $ENV{LD} : $CC; +$CFLAGS = $ENV{CFLAGS} ? "$ENV{CFLAGS}" : ""; +$LDFLAGS = $ENV{LDFLAGS} ? $ENV{LDFLAGS} : ""; + +my $o_file; +if ($CC =~ /libtool/) { + $o_file = "$MODULE-scan.lo" +} else { + $o_file = "$MODULE-scan.o" +} + +my $stdout=""; +if (!defined($VERBOSE) or $VERBOSE eq "0") { + $stdout=">/dev/null"; +} + +# Compiling scanner +$command = "$CC $stdout $CFLAGS -c -o $o_file $MODULE-scan.c"; +system("($command)") == 0 or die "Compilation of scanner failed: $!\n"; + +# Linking scanner +$command = "$LD $stdout -o $MODULE-scan $o_file $LDFLAGS"; +system($command) == 0 or die "Linking of scanner failed: $!\n"; + +# Running scanner $MODULE-scan "; +system("sh -c ./$MODULE-scan") == 0 or die "Scan failed: $!\n"; + +if (!defined($ENV{"GTK_DOC_KEEP_INTERMEDIATE"})) { + unlink "./$MODULE-scan.c", "./$MODULE-scan.o", "./$MODULE-scan.lo", "./$MODULE-scan"; +} + +&UpdateFileIfChanged ($old_hierarchy_filename, $new_hierarchy_filename, 0); +# we will merge these in scangobj-merge.py +#&UpdateFileIfChanged ($old_interfaces_filename, $new_interfaces_filename, 0); +#&UpdateFileIfChanged ($old_prerequisites_filename, $new_prerequisites_filename, 0); +#&UpdateFileIfChanged ($old_signals_filename, $new_signals_filename, 0); +#&UpdateFileIfChanged ($old_args_filename, $new_args_filename, 0); + diff --git a/validate/common/gtk-doc-plugins.mak b/validate/common/gtk-doc-plugins.mak new file mode 100644 index 0000000..f19e7d7 --- /dev/null +++ b/validate/common/gtk-doc-plugins.mak @@ -0,0 +1,382 @@ +# This is an include file specifically tuned for building documentation +# for GStreamer plug-ins + +help: + @echo + @echo "If you are a doc maintainer, run 'make update' to update" + @echo "the documentation files maintained in git" + @echo + @echo Other useful make targets: + @echo + @echo check-inspected-versions: make sure the inspected plugin info + @echo is up to date before a release + @echo + +# update the stuff maintained by doc maintainers +update: scanobj-update + $(MAKE) check-outdated-docs + +# We set GPATH here; this gives us semantics for GNU make +# which are more like other make's VPATH, when it comes to +# whether a source that is a target of one rule is then +# searched for in VPATH/GPATH. +# +GPATH = $(srcdir) + +# thomas: make docs parallel installable +TARGET_DIR=$(HTML_DIR)/$(DOC_MODULE)-@GST_API_VERSION@ + +MAINTAINER_DOC_STAMPS = \ + scanobj-build.stamp + +EXTRA_DIST = \ + $(MAINTAINER_DOC_STAMPS) \ + $(srcdir)/inspect/*.xml \ + $(SCANOBJ_FILES) \ + $(content_files) \ + $(extra_files) \ + $(HTML_IMAGES) \ + $(DOC_MAIN_SGML_FILE) \ + $(DOC_OVERRIDES) \ + $(DOC_MODULE)-sections.txt + +# we don't add scanobj-build.stamp here since they are built manually by docs +# maintainers and result is commited to git +DOC_STAMPS = \ + scan-build.stamp \ + tmpl-build.stamp \ + sgml-build.stamp \ + html-build.stamp \ + scan.stamp \ + tmpl.stamp \ + sgml.stamp \ + html.stamp + +# files generated/updated by gtkdoc-scangobj +SCANOBJ_FILES = \ + $(DOC_MODULE).args \ + $(DOC_MODULE).hierarchy \ + $(DOC_MODULE).interfaces \ + $(DOC_MODULE).prerequisites \ + $(DOC_MODULE).signals \ + $(DOC_MODULE).types + +SCANOBJ_FILES_O = \ + .libs/$(DOC_MODULE)-scan.o + +# files generated/updated by gtkdoc-scan +SCAN_FILES = \ + $(DOC_MODULE)-sections.txt \ + $(DOC_MODULE)-overrides.txt \ + $(DOC_MODULE)-decl.txt \ + $(DOC_MODULE)-decl-list.txt + + +REPORT_FILES = \ + $(DOC_MODULE)-undocumented.txt \ + $(DOC_MODULE)-undeclared.txt \ + $(DOC_MODULE)-unused.txt + +CLEANFILES = \ + $(SCANOBJ_FILES_O) \ + $(REPORT_FILES) \ + $(DOC_STAMPS) \ + inspect-registry.xml + +INSPECT_DIR = inspect + +if ENABLE_GTK_DOC +all-local: html-build.stamp + +### inspect GStreamer plug-ins; done by documentation maintainer ### + +# only look at the plugins in this module when building inspect .xml stuff +INSPECT_REGISTRY=$(top_builddir)/docs/plugins/inspect-registry.xml +INSPECT_ENVIRONMENT=\ + LC_ALL=C \ + GST_PLUGIN_SYSTEM_PATH_1_0= \ + GST_PLUGIN_PATH_1_0=$(top_builddir)/gst:$(top_builddir)/sys:$(top_builddir)/ext:$(top_builddir)/plugins:$(top_builddir)/src:$(top_builddir)/gnl \ + GST_REGISTRY_1_0=$(INSPECT_REGISTRY) \ + PKG_CONFIG_PATH="$(GST_PKG_CONFIG_PATH)" \ + $(INSPECT_EXTRA_ENVIRONMENT) + +#### scan gobjects; done by documentation maintainer #### +scanobj-update: + -rm scanobj-build.stamp + $(MAKE) scanobj-build.stamp + +# gstdoc-scanobj produces 5 output files (.new) +# scangobj-merge.py merges them into the file which we commit later +# TODO: also merge the hierarchy +scanobj-build.stamp: $(SCANOBJ_DEPS) $(basefiles) + @echo " DOC Introspecting gobjects" + @if test x"$(srcdir)" != x. ; then \ + for f in $(SCANOBJ_FILES) $(SCAN_FILES); \ + do \ + if test -e $(srcdir)/$$f; then cp -u $(srcdir)/$$f . ; fi; \ + done; \ + fi; \ + mkdir -p $(INSPECT_DIR); \ + scanobj_options=""; \ + if test "x$(V)" = "x1"; then \ + scanobj_options="--verbose"; \ + fi; \ + $(INSPECT_ENVIRONMENT) \ + CC="$(GTKDOC_CC)" LD="$(GTKDOC_LD)" \ + CFLAGS="$(GTKDOC_CFLAGS) $(CFLAGS) $(WARNING_CFLAGS)" \ + LDFLAGS="$(GTKDOC_LIBS) $(LDFLAGS)" \ + $(GST_DOC_SCANOBJ) $$scanobj_options --type-init-func="gst_init(NULL,NULL)" \ + --module=$(DOC_MODULE) --source=$(PACKAGE) --inspect-dir=$(INSPECT_DIR) && \ + echo " DOC Merging introspection data" && \ + $(PYTHON) \ + $(top_srcdir)/common/scangobj-merge.py $(DOC_MODULE) || exit 1; \ + if test x"$(srcdir)" != x. ; then \ + for f in $(SCANOBJ_FILES); \ + do \ + cmp -s ./$$f $(srcdir)/$$f || cp ./$$f $(srcdir)/ ; \ + done; \ + fi; \ + touch scanobj-build.stamp + +$(DOC_MODULE)-decl.txt $(SCANOBJ_FILES) $(SCANOBJ_FILES_O): scan-build.stamp + @true + +### scan headers; done on every build ### +scan-build.stamp: $(HFILE_GLOB) $(EXTRA_HFILES) $(basefiles) scanobj-build.stamp + @echo ' DOC Scanning header files' + @if test x"$(srcdir)" != x. ; then \ + for f in $(SCANOBJ_FILES) $(SCAN_FILES); \ + do \ + if test -e $(srcdir)/$$f; then cp -u $(srcdir)/$$f . ; fi; \ + done; \ + fi + @_source_dir='' ; \ + for i in $(DOC_SOURCE_DIR) ; do \ + _source_dir="$${_source_dir} --source-dir=$$i" ; \ + done ; \ + gtkdoc-scan \ + $(SCAN_OPTIONS) $(EXTRA_HFILES) \ + --module=$(DOC_MODULE) \ + $${_source_dir} \ + --ignore-headers="$(IGNORE_HFILES)"; \ + touch scan-build.stamp + +#### update templates; done on every build #### + +# in a non-srcdir build, we need to copy files from the previous step +# and the files from previous runs of this step +tmpl-build.stamp: $(DOC_MODULE)-decl.txt $(SCANOBJ_FILES) $(DOC_MODULE)-sections.txt $(DOC_OVERRIDES) + @echo ' DOC Rebuilding template files' + @if test x"$(srcdir)" != x. ; then \ + for f in $(SCANOBJ_FILES) $(SCAN_FILES); \ + do \ + if test -e $(srcdir)/$$f; then cp -u $(srcdir)/$$f . ; fi; \ + done; \ + fi + @gtkdoc-mktmpl --module=$(DOC_MODULE) + @$(PYTHON) \ + $(top_srcdir)/common/mangle-tmpl.py $(srcdir)/$(INSPECT_DIR) tmpl + @touch tmpl-build.stamp + +tmpl.stamp: tmpl-build.stamp + @true + +#### xml #### + +sgml-build.stamp: tmpl.stamp scan-build.stamp $(CFILE_GLOB) $(top_srcdir)/common/plugins.xsl $(expand_content_files) + @echo ' DOC Building XML' + @-mkdir -p xml + @for a in $(srcdir)/$(INSPECT_DIR)/*.xml; do \ + xsltproc --stringparam module $(MODULE) \ + $(top_srcdir)/common/plugins.xsl $$a > xml/`basename $$a`; done + @for f in $(EXAMPLE_CFILES); do \ + $(PYTHON) $(top_srcdir)/common/c-to-xml.py $$f > xml/element-`basename $$f .c`.xml; done + @gtkdoc-mkdb \ + --module=$(DOC_MODULE) \ + --source-dir=$(DOC_SOURCE_DIR) \ + --expand-content-files="$(expand_content_files)" \ + --main-sgml-file=$(srcdir)/$(DOC_MAIN_SGML_FILE) \ + --output-format=xml \ + --ignore-files="$(IGNORE_HFILES) $(IGNORE_CFILES)" \ + $(MKDB_OPTIONS) + @cp ../version.entities xml + @touch sgml-build.stamp + +sgml.stamp: sgml-build.stamp + @true + +#### html #### + +html-build.stamp: sgml.stamp $(DOC_MAIN_SGML_FILE) $(content_files) + @echo ' DOC Building HTML' + @rm -rf html + @mkdir html + @cp $(srcdir)/$(DOC_MAIN_SGML_FILE) html + @for f in $(content_files); do cp $(srcdir)/$$f html; done + @cp -pr xml html + @cp ../version.entities html + @mkhtml_options=""; \ + gtkdoc-mkhtml 2>&1 --help | grep >/dev/null "\-\-verbose"; \ + if test "$(?)" = "0"; then \ + if test "x$(V)" = "x1"; then \ + mkhtml_options="$$mkhtml_options --verbose"; \ + fi; \ + fi; \ + cd html && gtkdoc-mkhtml $$mkhtml_options $(DOC_MODULE)-@GST_API_VERSION@ $(DOC_MAIN_SGML_FILE) + @rm -f html/$(DOC_MAIN_SGML_FILE) + @rm -rf html/xml + @rm -f html/version.entities + @test "x$(HTML_IMAGES)" = "x" || for i in "" $(HTML_IMAGES) ; do \ + if test "$$i" != ""; then cp $(srcdir)/$$i html ; fi; done + @echo ' DOC Fixing cross-references' + @gtkdoc-fixxref --module=$(DOC_MODULE) --module-dir=html --html-dir=$(HTML_DIR) $(FIXXREF_OPTIONS) + @touch html-build.stamp + +clean-local-gtkdoc: + @rm -rf xml tmpl html +# clean files copied for nonsrcdir templates build + @if test x"$(srcdir)" != x. ; then \ + rm -rf $(SCANOBJ_FILES) $(SCAN_FILES) $(REPORT_FILES) \ + $(MAINTAINER_DOC_STAMPS); \ + fi +else +all-local: +clean-local-gtkdoc: +endif + +clean-local: clean-local-gtkdoc + @rm -f *~ *.bak + @rm -rf .libs + +distclean-local: + @rm -f $(REPORT_FILES) \ + $(DOC_MODULE)-decl-list.txt $(DOC_MODULE)-decl.txt + @rm -rf tmpl/*.sgml.bak + @rm -f $(DOC_MODULE).hierarchy + @rm -f *.stamp || true + @if test "$(abs_srcdir)" != "$(abs_builddir)" ; then \ + rm -f $(DOC_MODULE)-docs.sgml ; \ + rm -f $(DOC_MODULE).types ; \ + rm -f $(DOC_MODULE).interfaces ; \ + rm -f $(DOC_MODULE)-overrides.txt ; \ + rm -f $(DOC_MODULE).prerequisites ; \ + rm -f $(DOC_MODULE)-sections.txt ; \ + rm -rf tmpl/*.sgml ; \ + rm -rf $(INSPECT_DIR); \ + fi + @rm -rf *.o + +MAINTAINERCLEANFILES = $(MAINTAINER_DOC_STAMPS) + +# thomas: make docs parallel installable; devhelp requires majorminor too +install-data-local: + (installfiles=`echo $(builddir)/html/*.sgml $(builddir)/html/*.html $(builddir)/html/*.png $(builddir)/html/*.css`; \ + if test "$$installfiles" = '$(builddir)/html/*.sgml $(builddir)/html/*.html $(builddir)/html/*.png $(builddir)/html/*.css'; \ + then echo '-- Nothing to install' ; \ + else \ + $(mkinstalldirs) $(DESTDIR)$(TARGET_DIR); \ + for i in $$installfiles; do \ + echo '-- Installing '$$i ; \ + $(INSTALL_DATA) $$i $(DESTDIR)$(TARGET_DIR); \ + done; \ + pngfiles=`echo ./html/*.png`; \ + if test "$$pngfiles" != './html/*.png'; then \ + for i in $$pngfiles; do \ + echo '-- Installing '$$i ; \ + $(INSTALL_DATA) $$i $(DESTDIR)$(TARGET_DIR); \ + done; \ + fi; \ + echo '-- Installing $(builddir)/html/$(DOC_MODULE)-@GST_API_VERSION@.devhelp2' ; \ + if test -e $(builddir)/html/$(DOC_MODULE)-@GST_API_VERSION@.devhelp2; then \ + $(INSTALL_DATA) $(builddir)/html/$(DOC_MODULE)-@GST_API_VERSION@.devhelp2 \ + $(DESTDIR)$(TARGET_DIR)/$(DOC_MODULE)-@GST_API_VERSION@.devhelp2; \ + fi; \ + $(GTKDOC_REBASE) --relative --dest-dir=$(DESTDIR) --html-dir=$(DESTDIR)$(TARGET_DIR) || true ; \ + fi) +uninstall-local: + if test -d $(DESTDIR)$(TARGET_DIR); then \ + rm -rf $(DESTDIR)$(TARGET_DIR)/*; \ + rmdir -p $(DESTDIR)$(TARGET_DIR) 2>/dev/null || true; \ + else \ + echo '-- Nothing to uninstall' ; \ + fi; + +# +# Checks +# +if ENABLE_GTK_DOC +check-hierarchy: $(DOC_MODULE).hierarchy + @if grep ' ' $(DOC_MODULE).hierarchy; then \ + echo "$(DOC_MODULE).hierarchy contains tabs, please fix"; \ + /bin/false; \ + fi + +check: check-hierarchy +endif + +# wildcard is apparently not portable to other makes, hence the use of find +inspect_files = $(shell find $(srcdir)/$(INSPECT_DIR) -name '*.xml') + +check-inspected-versions: + @echo Checking plugin versions of inspected plugin data ...; \ + fail=0 ; \ + for each in $(inspect_files) ; do \ + if (grep -H '' $$each | grep -v '$(VERSION)'); then \ + echo $$each should be fixed to say version $(VERSION) or be removed ; \ + echo "sed -i -e 's//$(VERSION)<\/version>/'" $$each; \ + echo ; \ + fail=1; \ + fi ; \ + done ; \ + exit $$fail + +check-outdated-docs: + $(AM_V_GEN)echo Checking for outdated plugin inspect data ...; \ + fail=0 ; \ + if [ -d $(top_srcdir)/.git/ ]; then \ + files=`find $(srcdir)/inspect/ -name '*xml'`; \ + for f in $$files; do \ + ver=`grep '$(PACKAGE_VERSION)' $$f`; \ + if test "x$$ver" = "x"; then \ + plugin=`echo $$f | sed -e 's/^.*plugin-//' -e 's/.xml//'`; \ + # echo "Checking $$plugin $$f"; \ + pushd "$(top_srcdir)" >/dev/null; \ + pinit=`git grep -A3 GST_PLUGIN_DEFINE -- ext/ gst/ sys/ | grep "\"$$plugin\""`; \ + popd >/dev/null; \ + # echo "[$$pinit]"; \ + if test "x$$pinit" = "x"; then \ + printf " **** outdated docs for plugin %-15s: %s\n" $$plugin $$f; \ + fail=1; \ + fi; \ + fi; \ + done; \ + fi ; \ + exit $$fail + +# +# Require gtk-doc when making dist +# +if ENABLE_GTK_DOC +dist-check-gtkdoc: +else +dist-check-gtkdoc: + @echo "*** gtk-doc must be installed and enabled in order to make dist" + @false +endif + +# FIXME: decide whether we want to dist generated html or not +# also this only works, if the project has been build before +# we could dist html only if its there, but that might lead to missing html in +# tarballs +dist-hook: dist-check-gtkdoc dist-hook-local + mkdir $(distdir)/html + cp html/* $(distdir)/html + -cp $(srcdir)/$(DOC_MODULE).types $(distdir)/ + -cp $(srcdir)/$(DOC_MODULE)-sections.txt $(distdir)/ + cd $(distdir) && rm -f $(DISTCLEANFILES) + -gtkdoc-rebase --online --relative --html-dir=$(distdir)/html + +.PHONY : dist-hook-local docs check-outdated-docs inspect + +# avoid spurious build errors when distchecking with -jN +.NOTPARALLEL: diff --git a/validate/common/gtk-doc.mak b/validate/common/gtk-doc.mak new file mode 100644 index 0000000..9d76889 --- /dev/null +++ b/validate/common/gtk-doc.mak @@ -0,0 +1,227 @@ +########################################################################### +# Everything below here is generic and you shouldn't need to change it. +########################################################################### +# thomas: except of course that we did + +# thomas: copied from glib-2 +# We set GPATH here; this gives us semantics for GNU make +# which are more like other make's VPATH, when it comes to +# whether a source that is a target of one rule is then +# searched for in VPATH/GPATH. +# +GPATH = $(srcdir) + +# thomas: make docs parallel installable +TARGET_DIR=$(HTML_DIR)/$(DOC_MODULE)-@GST_API_VERSION@ + +EXTRA_DIST = \ + $(content_files) \ + $(extra_files) \ + $(HTML_IMAGES) \ + $(DOC_MAIN_SGML_FILE) \ + $(DOC_MODULE).types \ + $(DOC_OVERRIDES) \ + $(DOC_MODULE)-sections.txt + +DOC_STAMPS = \ + setup-build.stamp \ + scan-build.stamp \ + sgml-build.stamp \ + html-build.stamp \ + sgml.stamp \ + html.stamp + +SCANOBJ_FILES = \ + $(DOC_MODULE).args \ + $(DOC_MODULE).hierarchy \ + $(DOC_MODULE).interfaces \ + $(DOC_MODULE).prerequisites \ + $(DOC_MODULE).signals \ + .libs/$(DOC_MODULE)-scan.o + +REPORT_FILES = \ + $(DOC_MODULE)-undocumented.txt \ + $(DOC_MODULE)-undeclared.txt \ + $(DOC_MODULE)-unused.txt + +CLEANFILES = $(SCANOBJ_FILES) $(REPORT_FILES) $(DOC_STAMPS) doc-registry.xml + +if ENABLE_GTK_DOC +all-local: html-build.stamp + +#### setup #### + +setup-build.stamp: $(content_files) + -@if test "$(abs_srcdir)" != "$(abs_builddir)" ; then \ + echo ' DOC Preparing build'; \ + files=`echo $(DOC_MAIN_SGML_FILE) $(DOC_OVERRIDES) $(DOC_MODULE)-sections.txt $(DOC_MODULE).types $(content_files)`; \ + if test "x$$files" != "x" ; then \ + for file in $$files ; do \ + test -f $(abs_srcdir)/$$file && \ + cp -pu $(abs_srcdir)/$$file $(abs_builddir)/ || true; \ + done; \ + fi; \ + fi + @touch setup-build.stamp + +#### scan #### + +# in the case of non-srcdir builds, the built gst directory gets added +# to gtk-doc scanning; but only then, to avoid duplicates +scan-build.stamp: $(HFILE_GLOB) $(CFILE_GLOB) + @echo ' DOC Scanning header files' + @_source_dir='' ; \ + for i in $(DOC_SOURCE_DIR) ; do \ + _source_dir="$${_source_dir} --source-dir=$$i" ; \ + done ; \ + gtkdoc-scan \ + $(SCAN_OPTIONS) $(EXTRA_HFILES) \ + --module=$(DOC_MODULE) \ + $${_source_dir} \ + --ignore-headers="$(IGNORE_HFILES)" + @if grep -l '^..*$$' $(DOC_MODULE).types > /dev/null; then \ + echo " DOC Introspecting gobjects"; \ + GST_PLUGIN_SYSTEM_PATH_1_0=`cd $(top_builddir) && pwd` \ + GST_PLUGIN_PATH_1_0= \ + GST_REGISTRY_1_0=doc-registry.xml \ + $(GTKDOC_EXTRA_ENVIRONMENT) \ + CC="$(GTKDOC_CC)" LD="$(GTKDOC_LD)" \ + CFLAGS="$(GTKDOC_CFLAGS) $(CFLAGS)" \ + LDFLAGS="$(GTKDOC_LIBS) $(LDFLAGS)" \ + gtkdoc-scangobj --type-init-func="gst_init(NULL,NULL)" \ + --module=$(DOC_MODULE) ; \ + else \ + for i in $(SCANOBJ_FILES) ; do \ + test -f $$i || touch $$i ; \ + done \ + fi + @touch scan-build.stamp + +$(DOC_MODULE)-decl.txt $(SCANOBJ_FILES) $(DOC_MODULE)-sections.txt $(DOC_MODULE)-overrides.txt: scan-build.stamp + @true + +#### xml #### + +sgml-build.stamp: setup-build.stamp $(DOC_MODULE)-decl.txt $(SCANOBJ_FILES) $(DOC_MODULE)-sections.txt $(expand_content_files) + @echo ' DOC Building XML' + @gtkdoc-mkdb --module=$(DOC_MODULE) --source-dir=$(DOC_SOURCE_DIR) --expand-content-files="$(expand_content_files)" --main-sgml-file=$(DOC_MAIN_SGML_FILE) --output-format=xml $(MKDB_OPTIONS) + @cp ../version.entities xml + @touch sgml-build.stamp + +sgml.stamp: sgml-build.stamp + @true + +#### html #### + +html-build.stamp: sgml.stamp $(DOC_MAIN_SGML_FILE) $(content_files) + @echo ' DOC Building HTML' + @rm -rf html + @mkdir html + @cp -pr xml html + @cp ../version.entities ./ + @mkhtml_options=""; \ + gtkdoc-mkhtml 2>&1 --help | grep >/dev/null "\-\-verbose"; \ + if test "$(?)" = "0"; then \ + if test "x$(V)" = "x1"; then \ + mkhtml_options="$$mkhtml_options --verbose"; \ + fi; \ + fi; \ + @gtkdoc-mkhtml 2>&1 --help | grep >/dev/null "\-\-path"; \ + if test "$(?)" = "0"; then \ + mkhtml_options=--path="$(abs_srcdir)"; \ + fi; \ + cd html && gtkdoc-mkhtml $$mkhtml_options $(MKHTML_OPTIONS) $(DOC_MODULE)-@GST_API_VERSION@ ../$(DOC_MAIN_SGML_FILE) + @rm -rf html/xml + @rm -f version.entities + @test "x$(HTML_IMAGES)" = "x" || ( cd $(srcdir) && cp $(HTML_IMAGES) $(abs_builddir)/html ) + @echo ' DOC Fixing cross-references' + @gtkdoc-fixxref --module=$(DOC_MODULE) --module-dir=html --html-dir=$(HTML_DIR) $(FIXXREF_OPTIONS) + @touch html-build.stamp + +clean-local-gtkdoc: + @rm -rf xml tmpl html +# clean files copied for nonsrcdir templates build + @if test x"$(srcdir)" != x. ; then \ + rm -rf $(DOC_MODULE).types; \ + fi +else +all-local: +clean-local-gtkdoc: +endif + +clean-local: clean-local-gtkdoc + @rm -f *~ *.bak + @rm -rf .libs + +distclean-local: + @rm -f $(REPORT_FILES) \ + $(DOC_MODULE)-decl-list.txt $(DOC_MODULE)-decl.txt + @rm -rf tmpl/*.sgml.bak + @rm -f $(DOC_MODULE).hierarchy + @rm -f *.stamp || true + @if test "$(abs_srcdir)" != "$(abs_builddir)" ; then \ + rm -f $(DOC_MAIN_SGML_FILE) ; \ + rm -f $(DOC_OVERRIDES) ; \ + rm -f $(DOC_MODULE).types ; \ + rm -f $(DOC_MODULE).interfaces ; \ + rm -f $(DOC_MODULE).prerequisites ; \ + rm -f $(DOC_MODULE)-sections.txt ; \ + rm -f $(content_files) ; \ + rm -rf tmpl/*.sgml ; \ + fi + @rm -rf *.o + +maintainer-clean-local: clean + @cd $(srcdir) && rm -rf html \ + xml $(DOC_MODULE)-decl-list.txt $(DOC_MODULE)-decl.txt + +# thomas: make docs parallel installable; devhelp requires majorminor too +install-data-local: + (installfiles=`echo $(builddir)/html/*.sgml $(builddir)/html/*.html $(builddir)/html/*.png $(builddir)/html/*.css`; \ + if test "$$installfiles" = '$(builddir)/html/*.sgml $(builddir)/html/*.html $(builddir)/html/*.png $(builddir)/html/*.css'; \ + then echo '-- Nothing to install' ; \ + else \ + $(mkinstalldirs) $(DESTDIR)$(TARGET_DIR); \ + for i in $$installfiles; do \ + echo '-- Installing '$$i ; \ + $(INSTALL_DATA) $$i $(DESTDIR)$(TARGET_DIR); \ + done; \ + echo '-- Installing $(builddir)/html/$(DOC_MODULE)-@GST_API_VERSION@.devhelp2' ; \ + if test -e $(builddir)/html/$(DOC_MODULE)-@GST_API_VERSION@.devhelp2; then \ + $(INSTALL_DATA) $(builddir)/html/$(DOC_MODULE)-@GST_API_VERSION@.devhelp2 \ + $(DESTDIR)$(TARGET_DIR)/$(DOC_MODULE)-@GST_API_VERSION@.devhelp2; \ + fi; \ + $(GTKDOC_REBASE) --relative --dest-dir=$(DESTDIR) --html-dir=$(DESTDIR)$(TARGET_DIR) || true ; \ + fi) +uninstall-local: + if test -d $(DESTDIR)$(TARGET_DIR); then \ + rm -rf $(DESTDIR)$(TARGET_DIR)/*; \ + rmdir -p $(DESTDIR)$(TARGET_DIR) 2>/dev/null || true; \ + else \ + echo '-- Nothing to uninstall' ; \ + fi; + + +# +# Require gtk-doc when making dist +# +if ENABLE_GTK_DOC +dist-check-gtkdoc: +else +dist-check-gtkdoc: + @echo "*** gtk-doc must be installed and enabled in order to make dist" + @false +endif + +dist-hook: dist-check-gtkdoc dist-hook-local + mkdir $(distdir)/html + cp html/* $(distdir)/html + -cp $(srcdir)/$(DOC_MODULE).types $(distdir)/ + -cp $(srcdir)/$(DOC_MODULE)-sections.txt $(distdir)/ + cd $(distdir) && rm -f $(DISTCLEANFILES) + -gtkdoc-rebase --online --relative --html-dir=$(distdir)/html + +.PHONY : dist-hook-local docs + +# avoid spurious build errors when distchecking with -jN +.NOTPARALLEL: diff --git a/validate/common/hooks/install-hooks b/validate/common/hooks/install-hooks new file mode 100755 index 0000000..38840ec --- /dev/null +++ b/validate/common/hooks/install-hooks @@ -0,0 +1,97 @@ +#!/bin/bash +# +# install-hooks +# +# install hooks on git server +# +# usage: install-hooks [GIT_REPOSITORY_DIR] + +set -e + +HOOKS="update post-receive" + +# if directory where git repositories are is specified, change into that +if ! test -z $1 ; then + cd $1; +fi + +# save it +gitrepodir=`pwd` + +# make backup of previous hooks +timestamp=`date +%F-%H%M%S` +for hook in $HOOKS; do + if [ -e $hook ]; then + cp $hook $hook.backup-$timestamp + fi +done + +# now check out the common module with the latest hooks +tmpdir=`mktemp -d` +if test -z $tmpdir; then + echo "Failed to create temp dir" + exit 1; +fi + +pushd $tmpdir >/dev/null +git clone --branch=master $gitrepodir/common + +# copy over latest hooks into directory with git repositories +for hook in $HOOKS; do + cp -v common/hooks/$hook.hook $gitrepodir/$hook +done + +# now switch back to directory where the git repositories are +popd >/dev/null + +# clean up temp checkout of common dir +rm -rf $tmpdir + +# remove backup again if it is identical to the new one +for hook in $HOOKS; do + if cmp $hook $hook.backup-$timestamp; then + rm $hook.backup-$timestamp + fi +done + +# sanity check if there are any git repositories in here +if ! ls *.git */*.git 2>/dev/null >&2 ; then + echo "No git repositories in $(pwd) ?!" + exit; +fi + +echo "git repository base dir: $gitrepodir" + +for repo in *.git */*.git ; do + if test "$repo" = "sdk/glib.git"; then + echo "Skipping $repo" + continue + fi + echo "Updating hooks in $repo" + for hook in $HOOKS; do + # make sure target hook script actually exists + if ! test -e $hook ; then + echo "Hook '$hook' does not exist in $gitrepodir. Aborting."; + exit 1; + fi + # create new hook + rm -f $repo/hooks/$hook.new + pushd $repo/hooks >/dev/null + if [ -e "../../$hook" ] ; then + ln -s "../../$hook" $hook.new + elif [ -e "../../../$hook" ] ; then + ln -s "../../../$hook" $hook.new + else + echo "could not find hook script in parent directories ?!" + exit 1 + fi + # put new hook in place atomically + #echo "installing $repo/hooks/$hook" + mv $hook.new $hook + # test it to make sure all is good + cat $hook >/dev/null + popd >/dev/null + done +done + +echo "Done" diff --git a/validate/common/hooks/post-receive.hook b/validate/common/hooks/post-receive.hook new file mode 100755 index 0000000..0951bda --- /dev/null +++ b/validate/common/hooks/post-receive.hook @@ -0,0 +1,293 @@ +#!/usr/bin/perl -w +# +# Copyright 2005 Alexandre Julliard +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. + +use strict; +use open ':utf8'; +use Encode 'encode'; +use Cwd 'realpath'; + +binmode STDIN, ':utf8'; +binmode STDOUT, ':utf8'; + +sub git_config($); +sub get_repos_name(); + +# some parameters you may want to change + +# set this to something that takes "-s" +my $mailer = "/usr/bin/mail"; + +# debug mode +my $debug = 0; + +# configuration parameters + +my $project_name = 'gstreamer'; + +# base URL of the gitweb repository browser +my $gitweb_url = "http://cgit.freedesktop.org/$project_name"; + + +# default repository name +my $repos_name = get_repos_name(); + +# max size of diffs in bytes +my $max_diff_size = 10000; + +# address for mail notices +my $commitlist_address = 'gstreamer-commits@lists.freedesktop.org'; +#my $commitlist_address = 'tpm'; + +# max number of individual notices before falling back to a single global notice +my $max_individual_notices = 100; + +# format an integer date + timezone as string +# algorithm taken from git's date.c +sub format_date($$) +{ + my ($time,$tz) = @_; + + if ($tz < 0) + { + my $minutes = (-$tz / 100) * 60 + (-$tz % 100); + $time -= $minutes * 60; + } + else + { + my $minutes = ($tz / 100) * 60 + ($tz % 100); + $time += $minutes * 60; + } + return gmtime($time) . sprintf " %+05d", $tz; +} + +# fetch a parameter from the git config file +sub git_config($) +{ + my ($param) = @_; + + open CONFIG, "-|" or exec "git", "config", $param; + my $ret = ; + chomp $ret if $ret; + close CONFIG or $ret = undef; + return $ret; +} + +# send an email notification +sub mail_notification($$$@) +{ + my ($name, $subject, $content_type, @text) = @_; + $subject = encode("MIME-Q",$subject); + if ($debug) + { + print "---------------------\n"; + print "To: $name\n"; + print "Subject: $subject\n"; + print "Content-Type: $content_type\n"; + print "\n", join("\n", @text), "\n"; + } + else + { + my $pid = open MAIL, "|-"; + return unless defined $pid; + if (!$pid) + { + exec $mailer, "-s", $subject, $name, or die "Cannot exec $mailer"; + } + print MAIL join("\n", @text), "\n"; + close MAIL; + } +} + +# get the default repository name (includes subdirectory, if any) +sub get_repos_name() +{ + my $dir = `git rev-parse --git-dir`; + chomp $dir; + my $repos = realpath($dir); + $repos =~ s/(.*?)((\.git\/)?\.git)$/$1/; + $repos =~ s%(.*\/$project_name)/([^/]+/?[^/]*)/?$%$2%; + return $repos; +} + +# extract the information from a commit or tag object and return a hash containing the various fields +sub get_object_info($) +{ + my $obj = shift; + my %info = (); + my @log = (); + my $do_log = 0; + + open TYPE, "-|" or exec "git", "cat-file", "-t", $obj or die "cannot run git-cat-file"; + my $type = ; + chomp $type; + close TYPE; + + open OBJ, "-|" or exec "git", "cat-file", $type, $obj or die "cannot run git-cat-file"; + while () + { + chomp; + if ($do_log) + { + last if /^-----BEGIN PGP SIGNATURE-----/; + push @log, $_; + } + elsif (/^(author|committer|tagger) ((.*)(<.*>)) (\d+) ([+-]\d+)$/) + { + $info{$1} = $2; + $info{$1 . "_name"} = $3; + $info{$1 . "_email"} = $4; + $info{$1 . "_date"} = $5; + $info{$1 . "_tz"} = $6; + } + elsif (/^tag (.*)$/) + { + $info{"tag"} = $1; + } + elsif (/^$/) { $do_log = 1; } + } + close OBJ; + + $info{"type"} = $type; + $info{"log"} = \@log; + return %info; +} + +# send a commit notice to a mailing list +sub send_commit_notice($$) +{ + my ($ref,$obj) = @_; + my %info = get_object_info($obj); + my @notice = (); + my $subject; + + printf "sending e-mail for $obj\n"; + + # TODO normal tags are not identified + if ($info{"type"} eq "tag") + { + push @notice, + "Module: $repos_name", + "Branch: $ref", + "Tag: $obj", + $gitweb_url ? "URL: $gitweb_url/tag/?id=$obj\n" : "", + "Tagger: " . $info{"tagger"}, + "Date: " . format_date($info{"tagger_date"},$info{"tagger_tz"}), + "", + join "\n", @{$info{"log"}}; + $subject = "Tag " . $info{"tag"} . ": " . ${$info{"log"}}[0]; + } + else + { + push @notice, + "Module: $repos_name", + "Branch: $ref", + "Commit: $obj", + $gitweb_url ? "URL: $gitweb_url/commit/?id=$obj\n" : "", + "Author: " . $info{"author"}, + "Date: " . format_date($info{"author_date"},$info{"author_tz"}), + "", + join "\n", @{$info{"log"}}, + "", + "---", + ""; + + open STAT, "-|" or exec "git", "diff-tree", "--stat", "-M", "--no-commit-id", $obj or die "cannot exec git-diff-tree"; + push @notice, join("", ); + close STAT; + + open DIFF, "-|" or exec "git", "diff-tree", "-p", "-M", "--no-commit-id", $obj or die "cannot exec git-diff-tree"; + my $diff = join( "", ); + close DIFF; + + if (($max_diff_size == -1) || (length($diff) < $max_diff_size)) + { + push @notice, $diff; + } + else + { + push @notice, "Diff: $gitweb_url/diff/?id=$obj" if $gitweb_url; + } + + if ($ref eq 'master') + { + $subject = $repos_name . ": " . ${$info{"log"}}[0]; + } + else + { + $subject = "[$ref] " . $repos_name . ": " . ${$info{"log"}}[0]; + } + } + + mail_notification($commitlist_address, $subject, "text/plain; charset=UTF-8", @notice); +} + +# send a global commit notice when there are too many commits for individual mails +sub send_global_notice($$$) +{ + my ($ref, $old_sha1, $new_sha1) = @_; + my @notice = (); + + open LIST, "-|" or exec "git", "rev-list", "--pretty", "^$old_sha1", "$new_sha1" or die "cannot exec git-rev-list"; + while () + { + chomp; + s/^commit /URL: $gitweb_url\/commit\/?id=/ if $gitweb_url; + push @notice, $_; + } + close LIST; + + mail_notification($commitlist_address, "New commits on branch $ref", "text/plain; charset=UTF-8", @notice); +} + +# send all the notices +sub send_all_notices($$$) +{ + my ($old_sha1, $new_sha1, $ref) = @_; + + $ref =~ s/^refs\/heads\///; + + if ($old_sha1 eq '0' x 40) # new ref + { + send_commit_notice( $ref, $new_sha1 ) if $commitlist_address; + return; + } + + my @commits = (); + + open LIST, "-|" or exec "git", "rev-list", "--topo-order", "^$old_sha1", "$new_sha1" or die "cannot exec git-rev-list"; + while () + { + chomp; + die "invalid commit $_" unless /^[0-9a-f]{40}$/; + unshift @commits, $_; + } + close LIST; + + if (@commits > $max_individual_notices) + { + send_global_notice( $ref, $old_sha1, $new_sha1 ) if $commitlist_address; + return; + } + + foreach my $commit (@commits) + { + send_commit_notice( $ref, $commit ) if $commitlist_address; + } +} + +# append repository path to URL +$gitweb_url .= "/$repos_name" if $gitweb_url; + +while (<>) +{ + chomp; + if (/^([0-9a-f]{40}) ([0-9a-f]{40}) (.*)$/) { send_all_notices( $1, $2, $3 ); } +} + +exit 0; diff --git a/validate/common/hooks/pre-commit.hook b/validate/common/hooks/pre-commit.hook new file mode 100755 index 0000000..3c1062b --- /dev/null +++ b/validate/common/hooks/pre-commit.hook @@ -0,0 +1,83 @@ +#!/bin/sh +# +# Check that the code follows a consistant code style +# + +# Check for existence of indent, and error out if not present. +# On some *bsd systems the binary seems to be called gnunindent, +# so check for that first. + +version=`gnuindent --version 2>/dev/null` +if test "x$version" = "x"; then + version=`gindent --version 2>/dev/null` + if test "x$version" = "x"; then + version=`indent --version 2>/dev/null` + if test "x$version" = "x"; then + echo "GStreamer git pre-commit hook:" + echo "Did not find GNU indent, please install it before continuing." + exit 1 + else + INDENT=indent + fi + else + INDENT=gindent + fi +else + INDENT=gnuindent +fi + +case `$INDENT --version` in + GNU*) + ;; + default) + echo "GStreamer git pre-commit hook:" + echo "Did not find GNU indent, please install it before continuing." + echo "(Found $INDENT, but it doesn't seem to be GNU indent)" + exit 1 + ;; +esac + +INDENT_PARAMETERS="--braces-on-if-line \ + --case-brace-indentation0 \ + --case-indentation2 \ + --braces-after-struct-decl-line \ + --line-length80 \ + --no-tabs \ + --cuddle-else \ + --dont-line-up-parentheses \ + --continuation-indentation4 \ + --honour-newlines \ + --tab-size8 \ + --indent-level2 \ + --leave-preprocessor-space" + +echo "--Checking style--" +for file in `git diff-index --cached --name-only HEAD --diff-filter=ACMR| grep "\.c$"` ; do + # nf is the temporary checkout. This makes sure we check against the + # revision in the index (and not the checked out version). + nf=`git checkout-index --temp ${file} | cut -f 1` + newfile=`mktemp /tmp/${nf}.XXXXXX` || exit 1 + $INDENT ${INDENT_PARAMETERS} \ + $nf -o $newfile 2>> /dev/null + # FIXME: Call indent twice as it tends to do line-breaks + # different for every second call. + $INDENT ${INDENT_PARAMETERS} \ + $newfile 2>> /dev/null + diff -u -p "${nf}" "${newfile}" + r=$? + rm "${newfile}" + rm "${nf}" + if [ $r != 0 ] ; then +echo "=================================================================================================" +echo " Code style error in: $file " +echo " " +echo " Please fix before committing. Don't forget to run git add before trying to commit again. " +echo " If the whole file is to be committed, this should work (run from the top-level directory): " +echo " " +echo " gst-indent $file; git add $file; git commit" +echo " " +echo "=================================================================================================" + exit 1 + fi +done +echo "--Checking style pass--" diff --git a/validate/common/hooks/pre-receive.hook b/validate/common/hooks/pre-receive.hook new file mode 100755 index 0000000..c0bde0a --- /dev/null +++ b/validate/common/hooks/pre-receive.hook @@ -0,0 +1,190 @@ +#!/usr/bin/env python +import sys +import os +import subprocess + +# This server-side pre-receive hook is to be put and activated in all modules +# that depend on the common submodule. +# +# It will check whether any modifications to the common 'submodule file' has a +# a valid commit SHA1 that exists in the common module. + +def commit_exists(sha1, gitdir): + """Returns True if the sha1 is a valid commit in the given + git directory""" + # FIXME: We're using 'git show' for the time being, but there's a small + # risk that there might be a valid SHA1 for a non-commit object. + env = os.environ.copy() + env["GIT_DIR"] = gitdir + sub = subprocess.Popen(["git", "show", sha1], env=env, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + while True: + res = sub.poll() + if res != None: + return res == 0 + +# location of the common git module +COMMON_GIT_DIR = "/git/gstreamer/common.git" + +# checks whether the push contains a change to the common submodule, +# and if so checks that it is changing it to a valid *EXISTING* common +# commit +def has_valid_common_change(old, new, ref): + # 1. Get the latest change to common (if there was any changes) + sub = subprocess.Popen(["git", "diff", "%s..%s" % (old, new, ), "--", "common"], + stdout=subprocess.PIPE) + stdout, stderr = sub.communicate() + + print "=> Checking for integrity of common submodule changes" + + if stdout != "": + # Format of submodule special files + # Subproject commit + + # we get a diff, therefore only grab the last line + lastline = stdout.strip().rsplit('\n',1)[-1] + if not lastline.startswith("+Subproject commit"): + # this is bad, it means the diff has changed to something completely + # different + return False + subsha1 = lastline.rsplit(' ', 1)[-1] + print " Pack wants common to point to", subsha1 + if not commit_exists(subsha1, COMMON_GIT_DIR): + print + print " /!\\ Commit does not exist in common git module /!\\" + print " /!\\ for ref", ref + print + print " Attempting to push commits containing modifications to common" + print " that have not been commited to the actuall common module." + print + print " First push your changes to common/ before pushing the changes" + print " to the module using it." + print + return False + + print " [OK]" + print + return True + +def commit_in_valid_branch(sha): + sub = subprocess.Popen(["git", "branch", "--contains", sha], + stdout=subprocess.PIPE) + stdout, stderr = sub.communicate() + r = stdout.strip() + return r != "" + +# checks whether the push contains merge commits and if so, makes sure that +# the merge is only between branches present on the repository. +def contains_valid_merge(old, new, ref): + sub = subprocess.Popen(["git", "rev-list", "--parents", "%s..%s" % (old, new, )], + stdout=subprocess.PIPE) + stdout, stderr = sub.communicate() + + res = True + + print "=> Checking for merge commits" + + for line in stdout.split('\n'): + if res == False: + break + splits = line.strip().split() + if len(splits) > 2: + # the commit has more than one parent => it's a merge + commit = splits[0] + for parent in splits[1:]: + if not commit_in_valid_branch(parent): + print + print " /!\\ Commit %s" % commit + print " /!\\ is a merge commit from/to a branch not" + print " /!\\ present on this repository" + print + print " Make sure you are not attempting to push a merge" + print " from a 3rd party repository" + print + print " Make sure you have properly rebased your commits against" + print " the current official branches" + print + res = False + break + + if res: + print " [OK]" + print + return res + +# checks whether the first line of any commit message contains a marker +# that indicates that it's work-in-progress that shouldn't be pushed +def contains_wip_commits(old, new): + # Get the commit message header + sub = subprocess.Popen(["git", "log", "--format=%s", "%s..%s" % (old, new, )], stdout=subprocess.PIPE) + stdout, stderr = sub.communicate() + if stdout != "": + for line in stdout.strip().rsplit('\n',1): + if line.startswith("!"): + return True + if line.startswith("WIP:") or line.startswith("wip:"): + return True + return False + +pushvalid = True +error_badcommon = False +error_badcommit = False + +openbranches = ["1.2", "1.0", "master"] + +def branch_closed(ref): + # We only care about branches + if not ref.startswith("refs/heads/"): + return False + + # Check if it's in the allowed branches + brname = ref.rsplit("/", 1)[-1].strip() + if brname in openbranches: + return False + + # Else it's not allowed + return True + +for line in sys.stdin.readlines(): + # ref is the ref to be modified + # old is the old commit it was pointing to + # new is the new commit it was pointing to + old, new, ref = line.split(' ', 2) + + pushvalid = has_valid_common_change(old, new, ref) + if pushvalid == False: + break + if branch_closed(ref): + print ">>>> Attempting to push to", ref + print ">>>> BRANCH READ-ONLY OR FORBIDDEN" + print ">>>> PUSH DENIED" + print + pushvalid = False + break + pushvalid = contains_valid_merge(old, new, ref) + if pushvalid == False: + break + if contains_wip_commits(old, new): + error_badcommit = True + pushvalid = False + break + +if pushvalid: + print " Incoming packet valid, proceeding to actual commit" + print + sys.exit(0) +else: + if error_badcommit: + print " Attempting to push commits with commit messages that start" + print " with '!' or 'WIP:'. Such commit messages are reserved for" + print " private branches and work in progress to prevent the commits" + print " from accidentally being pushed to the main repository." + if error_badcommon: + print " Attempting to push commits containing modifications to common" + print " that have not been commited to the actuall common module." + print + print " First push your changes to common/ before pushing the changes" + print " to the module using it." + sys.exit(-1) + diff --git a/validate/common/hooks/update.hook b/validate/common/hooks/update.hook new file mode 100755 index 0000000..d5a3331 --- /dev/null +++ b/validate/common/hooks/update.hook @@ -0,0 +1,128 @@ +#!/bin/sh +# +# An example hook script to blocks unannotated tags from entering. +# Called by "git receive-pack" with arguments: refname sha1-old sha1-new +# +# To enable this hook, rename this file to "update". +# +# Config +# ------ +# hooks.allowunannotated +# This boolean sets whether unannotated tags will be allowed into the +# repository. By default they won't be. +# hooks.allowdeletetag +# This boolean sets whether deleting tags will be allowed in the +# repository. By default they won't be. +# hooks.allowmodifytag +# This boolean sets whether a tag may be modified after creation. By default +# it won't be. +# hooks.allowdeletebranch +# This boolean sets whether deleting branches will be allowed in the +# repository. By default they won't be. +# hooks.denycreatebranch +# This boolean sets whether remotely creating branches will be denied +# in the repository. By default this is allowed. +# + +# --- Command line +refname="$1" +oldrev="$2" +newrev="$3" + +# --- Safety check +if [ -z "$GIT_DIR" ]; then + echo "Don't run this script from the command line." >&2 + echo " (if you want, you could supply GIT_DIR then run" >&2 + echo " $0 )" >&2 + exit 1 +fi + +if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +# --- Config +allowunannotated=false #$(git config --bool hooks.allowunannotated) +allowdeletebranch=false #$(git config --bool hooks.allowdeletebranch) +denycreatebranch=false #$(git config --bool hooks.denycreatebranch) +allowdeletetag=false #$(git config --bool hooks.allowdeletetag) +allowmodifytag=true #$(git config --bool hooks.allowmodifytag) + +# check for no description +projectdesc=$(sed -e '1q' "$GIT_DIR/description") +case "$projectdesc" in +"Unnamed repository"* | "") + echo "*** Project description file hasn't been set" >&2 + exit 1 + ;; +esac + +# --- Check types +# if $newrev is 0000...0000, it's a commit to delete a ref. +zero="0000000000000000000000000000000000000000" +if [ "$newrev" = "$zero" ]; then + newrev_type=delete +else + newrev_type=$(git cat-file -t $newrev) +fi + +case "$refname","$newrev_type" in + refs/tags/*,commit) + # un-annotated tag + short_refname=${refname##refs/tags/} + if [ "$allowunannotated" != "true" ]; then + echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2 + echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 + exit 1 + fi + ;; + refs/tags/*,delete) + # delete tag + if [ "$allowdeletetag" != "true" ]; then + echo "*** Deleting a tag is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/tags/*,tag) + # annotated tag + if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 + then + echo "*** Tag '$refname' already exists." >&2 + echo "*** Modifying a tag is not allowed in this repository." >&2 + exit 1 + fi + ;; + refs/heads/*,commit) + # branch + if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then + echo "*** Creating a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/heads/*,delete) + # delete branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/remotes/*,commit) + # tracking branch + ;; + refs/remotes/*,delete) + # delete tracking branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a tracking branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + *) + # Anything else (is there anything else?) + echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 + exit 1 + ;; +esac + +# --- Finished +exit 0 diff --git a/validate/common/m4/.gitignore b/validate/common/m4/.gitignore new file mode 100644 index 0000000..b36621e --- /dev/null +++ b/validate/common/m4/.gitignore @@ -0,0 +1,23 @@ +codeset.m4 +gettext.m4 +glibc21.m4 +iconv.m4 +intdiv0.m4 +inttypes-pri.m4 +inttypes.m4 +inttypes_h.m4 +isc-posix.m4 +lcmessage.m4 +lib-ld.m4 +lib-link.m4 +lib-prefix.m4 +progtest.m4 +stdint_h.m4 +uintmax_t.m4 +ulonglong.m4 + +libtool.m4 +ltoptions.m4 +ltsugar.m4 +ltversion.m4 + diff --git a/validate/common/m4/Makefile.am b/validate/common/m4/Makefile.am new file mode 100644 index 0000000..cdcec8c --- /dev/null +++ b/validate/common/m4/Makefile.am @@ -0,0 +1,41 @@ +EXTRA_DIST = \ + README \ + as-ac-expand.m4 \ + as-auto-alt.m4 \ + as-compiler-flag.m4 \ + as-compiler.m4 \ + as-docbook.m4 \ + as-gcc-inline-assembly.m4 \ + as-libtool.m4 \ + as-libtool-tags.m4 \ + as-python.m4 \ + as-version.m4 \ + ax_create_stdint_h.m4 \ + ax_pthread.m4 \ + glib-gettext.m4 \ + gst-arch.m4 \ + gst-args.m4 \ + gst-check.m4 \ + gst-debuginfo.m4 \ + gst-default.m4 \ + gst-doc.m4 \ + gst-dowhile.m4 \ + gst-error.m4 \ + gst-feature.m4 \ + gst-function.m4 \ + gst-gettext.m4 \ + gst-glib2.m4 \ + gst-libxml2.m4 \ + gst-parser.m4 \ + gst-package-release-datetime.m4 \ + gst-platform.m4 \ + gst-plugindir.m4 \ + gst-plugin-docs.m4 \ + gst-valgrind.m4 \ + gst-x11.m4 \ + gst.m4 \ + gtk-doc.m4 \ + introspection.m4 \ + pkg.m4 \ + check.m4 \ + orc.m4 diff --git a/validate/common/m4/README b/validate/common/m4/README new file mode 100644 index 0000000..867a344 --- /dev/null +++ b/validate/common/m4/README @@ -0,0 +1,3 @@ +All aclocal .m4 files we need are put here and cat'd to acinclude.m4 in +the source root. Official ones (taken from the relevant devel packages) +are named as-is, unofficial ones (or changed ones) get a gst-prefix. diff --git a/validate/common/m4/as-ac-expand.m4 b/validate/common/m4/as-ac-expand.m4 new file mode 100644 index 0000000..d6c9e33 --- /dev/null +++ b/validate/common/m4/as-ac-expand.m4 @@ -0,0 +1,43 @@ +dnl as-ac-expand.m4 0.2.0 +dnl autostars m4 macro for expanding directories using configure's prefix +dnl thomas@apestaart.org + +dnl AS_AC_EXPAND(VAR, CONFIGURE_VAR) +dnl example +dnl AS_AC_EXPAND(SYSCONFDIR, $sysconfdir) +dnl will set SYSCONFDIR to /usr/local/etc if prefix=/usr/local + +AC_DEFUN([AS_AC_EXPAND], +[ + EXP_VAR=[$1] + FROM_VAR=[$2] + + dnl first expand prefix and exec_prefix if necessary + prefix_save=$prefix + exec_prefix_save=$exec_prefix + + dnl if no prefix given, then use /usr/local, the default prefix + if test "x$prefix" = "xNONE"; then + prefix="$ac_default_prefix" + fi + dnl if no exec_prefix given, then use prefix + if test "x$exec_prefix" = "xNONE"; then + exec_prefix=$prefix + fi + + full_var="$FROM_VAR" + dnl loop until it doesn't change anymore + while true; do + new_full_var="`eval echo $full_var`" + if test "x$new_full_var" = "x$full_var"; then break; fi + full_var=$new_full_var + done + + dnl clean up + full_var=$new_full_var + AC_SUBST([$1], "$full_var") + + dnl restore prefix and exec_prefix + prefix=$prefix_save + exec_prefix=$exec_prefix_save +]) diff --git a/validate/common/m4/as-auto-alt.m4 b/validate/common/m4/as-auto-alt.m4 new file mode 100644 index 0000000..3f7920d --- /dev/null +++ b/validate/common/m4/as-auto-alt.m4 @@ -0,0 +1,50 @@ +dnl as-auto-alt.m4 0.0.2 +dnl autostars m4 macro for supplying alternate autotools versions to configure +dnl thomas@apestaart.org +dnl +dnl AS_AUTOTOOLS_ALTERNATE() +dnl +dnl supplies --with arguments for autoconf, autoheader, automake, aclocal + +AC_DEFUN([AS_AUTOTOOLS_ALTERNATE], +[ + dnl allow for different autoconf version + AC_ARG_WITH(autoconf, + AC_HELP_STRING([--with-autoconf], + [use a different autoconf for regeneration of Makefiles]), + [ + unset AUTOCONF + AM_MISSING_PROG(AUTOCONF, ${withval}) + AC_MSG_NOTICE([Using $AUTOCONF as autoconf]) + ]) + + dnl allow for different autoheader version + AC_ARG_WITH(autoheader, + AC_HELP_STRING([--with-autoheader], + [use a different autoheader for regeneration of Makefiles]), + [ + unset AUTOHEADER + AM_MISSING_PROG(AUTOHEADER, ${withval}) + AC_MSG_NOTICE([Using $AUTOHEADER as autoheader]) + ]) + + dnl allow for different automake version + AC_ARG_WITH(automake, + AC_HELP_STRING([--with-automake], + [use a different automake for regeneration of Makefiles]), + [ + unset AUTOMAKE + AM_MISSING_PROG(AUTOMAKE, ${withval}) + AC_MSG_NOTICE([Using $AUTOMAKE as automake]) + ]) + + dnl allow for different aclocal version + AC_ARG_WITH(aclocal, + AC_HELP_STRING([--with-aclocal], + [use a different aclocal for regeneration of Makefiles]), + [ + unset ACLOCAL + AM_MISSING_PROG(ACLOCAL, ${withval}) + AC_MSG_NOTICE([Using $ACLOCAL as aclocal]) + ]) +]) diff --git a/validate/common/m4/as-compiler-flag.m4 b/validate/common/m4/as-compiler-flag.m4 new file mode 100644 index 0000000..8bb853a --- /dev/null +++ b/validate/common/m4/as-compiler-flag.m4 @@ -0,0 +1,96 @@ +dnl as-compiler-flag.m4 0.1.0 + +dnl autostars m4 macro for detection of compiler flags + +dnl David Schleef +dnl Tim-Philipp Müller + +dnl AS_COMPILER_FLAG(CFLAGS, ACTION-IF-ACCEPTED, [ACTION-IF-NOT-ACCEPTED]) +dnl Tries to compile with the given CFLAGS. +dnl Runs ACTION-IF-ACCEPTED if the compiler can compile with the flags, +dnl and ACTION-IF-NOT-ACCEPTED otherwise. + +AC_DEFUN([AS_COMPILER_FLAG], +[ + AC_MSG_CHECKING([to see if compiler understands $1]) + + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $1" + + AC_TRY_COMPILE([ ], [], [flag_ok=yes], [flag_ok=no]) + CFLAGS="$save_CFLAGS" + + if test "X$flag_ok" = Xyes ; then + $2 + true + else + $3 + true + fi + AC_MSG_RESULT([$flag_ok]) +]) + +dnl AS_CXX_COMPILER_FLAG(CPPFLAGS, ACTION-IF-ACCEPTED, [ACTION-IF-NOT-ACCEPTED]) +dnl Tries to compile with the given CPPFLAGS. +dnl Runs ACTION-IF-ACCEPTED if the compiler can compile with the flags, +dnl and ACTION-IF-NOT-ACCEPTED otherwise. + +AC_DEFUN([AS_CXX_COMPILER_FLAG], +[ + AC_REQUIRE([AC_PROG_CXX]) + + AC_MSG_CHECKING([to see if c++ compiler understands $1]) + + save_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $1" + + AC_LANG_PUSH(C++) + + AC_TRY_COMPILE([ ], [], [flag_ok=yes], [flag_ok=no]) + CPPFLAGS="$save_CPPFLAGS" + + if test "X$flag_ok" = Xyes ; then + $2 + true + else + $3 + true + fi + + AC_LANG_POP(C++) + + AC_MSG_RESULT([$flag_ok]) +]) + +dnl AS_OBJC_COMPILER_FLAG(CPPFLAGS, ACTION-IF-ACCEPTED, [ACTION-IF-NOT-ACCEPTED]) +dnl Tries to compile with the given CPPFLAGS. +dnl Runs ACTION-IF-ACCEPTED if the compiler can compile with the flags, +dnl and ACTION-IF-NOT-ACCEPTED otherwise. + +AC_DEFUN([AS_OBJC_COMPILER_FLAG], +[ + AC_REQUIRE([AC_PROG_OBJC]) + + AC_MSG_CHECKING([to see if Objective C compiler understands $1]) + + save_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $1" + + AC_LANG_PUSH([Objective C]) + + AC_TRY_COMPILE([ ], [], [flag_ok=yes], [flag_ok=no]) + CPPFLAGS="$save_CPPFLAGS" + + if test "X$flag_ok" = Xyes ; then + $2 + true + else + $3 + true + fi + + AC_LANG_POP([Objective C]) + + AC_MSG_RESULT([$flag_ok]) +]) + diff --git a/validate/common/m4/as-compiler.m4 b/validate/common/m4/as-compiler.m4 new file mode 100644 index 0000000..309a060 --- /dev/null +++ b/validate/common/m4/as-compiler.m4 @@ -0,0 +1,44 @@ +dnl as-compiler.m4 0.1.0 + +dnl autostars m4 macro for detection of compiler flavor + +dnl Thomas Vander Stichele + +dnl $Id: as-compiler.m4,v 1.4 2004/06/01 09:44:19 thomasvs Exp $ + +dnl AS_COMPILER(COMPILER) +dnl will set variable COMPILER to +dnl - gcc +dnl - forte +dnl - (empty) if no guess could be made + +AC_DEFUN([AS_COMPILER], +[ + as_compiler= + AC_MSG_CHECKING(for compiler flavour) + + dnl is it gcc ? + if test "x$GCC" = "xyes"; then + as_compiler="gcc" + fi + + dnl is it forte ? + AC_TRY_RUN([ +int main +(int argc, char *argv[]) +{ +#ifdef __sun + return 0; +#else + return 1; +#endif +} + ], as_compiler="forte", ,) + + if test "x$as_compiler" = "x"; then + AC_MSG_RESULT([unknown !]) + else + AC_MSG_RESULT($as_compiler) + fi + [$1]=$as_compiler +]) diff --git a/validate/common/m4/as-docbook.m4 b/validate/common/m4/as-docbook.m4 new file mode 100644 index 0000000..2e27050 --- /dev/null +++ b/validate/common/m4/as-docbook.m4 @@ -0,0 +1,78 @@ +dnl AS_DOCBOOK([, ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]) +dnl checks if xsltproc can build docbook documentation +dnl (which is possible if the catalog is set up properly +dnl I also tried checking for a specific version and type of docbook +dnl but xsltproc seemed to happily run anyway, so we can't check for that +dnl and version +dnl this macro takes inspiration from +dnl http://www.movement.uklinux.net/docs/docbook-autotools/configure.html +AC_DEFUN([AS_DOCBOOK], +[ + XSLTPROC_FLAGS=--nonet + DOCBOOK_ROOT= + TYPE_LC=xml + TYPE_UC=XML + DOCBOOK_VERSION=4.1.2 + + if test -n "$XML_CATALOG_FILES"; then + oldIFS=$IFS + IFS=' ' + for xml_catalog_file in $XML_CATALOG_FILES; do + if test -f $xml_catalog_file; then + XML_CATALOG=$xml_catalog_file + CAT_ENTRY_START='' + break + fi + done + IFS=$oldIFS + elif test ! -f /etc/xml/catalog; then + for i in /usr/share/sgml/docbook/stylesheet/xsl/nwalsh /usr/share/sgml/docbook/xsl-stylesheets/ /usr/local/share/xsl/docbook ; + do + if test -d "$i"; then + DOCBOOK_ROOT=$i + fi + done + else + XML_CATALOG=/etc/xml/catalog + CAT_ENTRY_START='' + fi + + dnl We need xsltproc to process the test + AC_CHECK_PROG(XSLTPROC,xsltproc,xsltproc,) + XSLTPROC_WORKS=no + if test -n "$XSLTPROC"; then + AC_MSG_CHECKING([whether xsltproc docbook processing works]) + + if test -n "$XML_CATALOG"; then + DB_FILE="http://docbook.sourceforge.net/release/xsl/current/xhtml/docbook.xsl" + else + DB_FILE="$DOCBOOK_ROOT/xhtml/docbook.xsl" + fi + $XSLTPROC $XSLTPROC_FLAGS $DB_FILE >/dev/null 2>&1 << END + + + + +END + if test "$?" = 0; then + XSLTPROC_WORKS=yes + fi + AC_MSG_RESULT($XSLTPROC_WORKS) + fi + + if test "x$XSLTPROC_WORKS" = "xyes"; then + dnl execute ACTION-IF-FOUND + ifelse([$1], , :, [$1]) + else + dnl execute ACTION-IF-NOT-FOUND + ifelse([$2], , :, [$2]) + fi + + AC_SUBST(XML_CATALOG) + AC_SUBST(XSLTPROC_FLAGS) + AC_SUBST(DOCBOOK_ROOT) + AC_SUBST(CAT_ENTRY_START) + AC_SUBST(CAT_ENTRY_END) +]) diff --git a/validate/common/m4/as-gcc-inline-assembly.m4 b/validate/common/m4/as-gcc-inline-assembly.m4 new file mode 100644 index 0000000..af32104 --- /dev/null +++ b/validate/common/m4/as-gcc-inline-assembly.m4 @@ -0,0 +1,52 @@ +dnl as-gcc-inline-assembly.m4 0.1.0 + +dnl autostars m4 macro for detection of gcc inline assembly + +dnl David Schleef + +dnl $Id$ + +dnl AS_COMPILER_FLAG(ACTION-IF-ACCEPTED, [ACTION-IF-NOT-ACCEPTED]) +dnl Tries to compile with the given CFLAGS. +dnl Runs ACTION-IF-ACCEPTED if the compiler can compile with the flags, +dnl and ACTION-IF-NOT-ACCEPTED otherwise. + +AC_DEFUN([AS_GCC_INLINE_ASSEMBLY], +[ + AC_MSG_CHECKING([if compiler supports gcc-style inline assembly]) + + AC_TRY_COMPILE([], [ +#ifdef __GNUC_MINOR__ +#if (__GNUC__ * 1000 + __GNUC_MINOR__) < 3004 +#error GCC before 3.4 has critical bugs compiling inline assembly +#endif +#endif +__asm__ (""::) ], [flag_ok=yes], [flag_ok=no]) + + if test "X$flag_ok" = Xyes ; then + $1 + true + else + $2 + true + fi + AC_MSG_RESULT([$flag_ok]) +]) + + +AC_DEFUN([AS_GCC_ASM_POWERPC_FPU], +[ + AC_MSG_CHECKING([if compiler supports FPU instructions on PowerPC]) + + AC_TRY_COMPILE([], [__asm__ ("fadd 0,0,0"::) ], [flag_ok=yes], [flag_ok=no]) + + if test "X$flag_ok" = Xyes ; then + $1 + true + else + $2 + true + fi + AC_MSG_RESULT([$flag_ok]) +]) + diff --git a/validate/common/m4/as-libtool-tags.m4 b/validate/common/m4/as-libtool-tags.m4 new file mode 100644 index 0000000..06f0ae4 --- /dev/null +++ b/validate/common/m4/as-libtool-tags.m4 @@ -0,0 +1,83 @@ +dnl as-libtool-tags.m4 0.1.4 + +dnl autostars m4 macro for selecting libtool "tags" (languages) + +dnl Andy Wingo does not claim credit for this macro +dnl backported from libtool 1.6 by Paolo Bonzini +dnl see http://lists.gnu.org/archive/html/libtool/2003-12/msg00007.html + +dnl $Id$ + +dnl AS_LIBTOOL_TAGS([tags...]) + +dnl example +dnl AS_LIBTOOL_TAGS([]) for only C (no fortran, etc) + +dnl When AC_LIBTOOL_TAGS is used, I redefine _LT_AC_TAGCONFIG +dnl to be more similar to the libtool 1.6 implementation, which +dnl uses an m4 loop and m4 case instead of a shell loop. This +dnl way the CXX/GCJ/F77/RC tests are not always expanded. + +dnl AS_LIBTOOL_TAGS +dnl --------------- +dnl tags to enable +AC_DEFUN([AS_LIBTOOL_TAGS], +[m4_define([_LT_TAGS],[$1]) +m4_define([_LT_AC_TAGCONFIG], [ + # redefined LT AC TAGCONFIG + if test -f "$ltmain"; then + if test ! -f "${ofile}"; then + AC_MSG_WARN([output file `$ofile' does not exist]) + fi + + if test -z "$LTCC"; then + eval "`$SHELL ${ofile} --config | grep '^LTCC='`" + if test -z "$LTCC"; then + AC_MSG_WARN([output file `$ofile' does not look like a libtool script]) + else + AC_MSG_WARN([using `LTCC=$LTCC', extracted from `$ofile']) + fi + fi + + AC_FOREACH([_LT_TAG], _LT_TAGS, + echo THOMAS: tag _LT_TAG + [m4_case(_LT_TAG, + [CXX], [ + if test -n "$CXX" && test "X$CXX" != "Xno"; then + echo "THOMAS: YAY CXX" + AC_LIBTOOL_LANG_CXX_CONFIG + available_tags="$available_tags _LT_TAG" + fi], + [F77], [ + if test -n "$F77" && test "X$F77" != "Xno"; then + AC_LIBTOOL_LANG_F77_CONFIG + available_tags="$available_tags _LT_TAG" + fi], + [GCJ], [ + if test -n "$GCJ" && test "X$GCJ" != "Xno"; then + AC_LIBTOOL_LANG_GCJ_CONFIG + available_tags="$available_tags _LT_TAG" + fi], + [RC], [ + if test -n "$RC" && test "X$RC" != "Xno"; then + AC_LIBTOOL_LANG_RC_CONFIG + available_tags="$available_tags _LT_TAG" + fi], + [m4_errprintn(m4_location[: error: invalid tag name: ]"_LT_TAG") + m4_exit(1)]) + ]) + echo THOMAS: available tags: $available_tags + fi + # Now substitute the updated list of available tags. + if eval "sed -e 's/^available_tags=.*\$/available_tags=\"$available_tags\"/' \"$ofile\" > \"${ofile}T\""; then + mv "${ofile}T" "$ofile" + chmod +x "$ofile" + AC_MSG_NOTICE([updated available libtool tags with $available_tags.]) + else + rm -f "${ofile}T" + AC_MSG_ERROR([unable to update list of available tagged configurations.]) + + fi + +])dnl _LT_AC_TAG_CONFIG +]) diff --git a/validate/common/m4/as-libtool.m4 b/validate/common/m4/as-libtool.m4 new file mode 100644 index 0000000..3b16095 --- /dev/null +++ b/validate/common/m4/as-libtool.m4 @@ -0,0 +1,46 @@ +dnl as-libtool.m4 0.1.4 + +dnl autostars m4 macro for libtool versioning + +dnl Thomas Vander Stichele + +dnl $Id: as-libtool.m4,v 1.10 2005/10/15 13:44:23 thomasvs Exp $ + +dnl AS_LIBTOOL(PREFIX, CURRENT, REVISION, AGE, [RELEASE]) + +dnl example +dnl AS_LIBTOOL(GST, 2, 0, 0) + +dnl this macro +dnl - defines [$PREFIX]_CURRENT, REVISION and AGE +dnl - defines [$PREFIX]_LIBVERSION +dnl - defines [$PREFIX]_LT_LDFLAGS to set versioning +dnl - AC_SUBST's them all + +dnl if RELEASE is given, then add a -release option to the LDFLAGS +dnl with the given release version +dnl then use [$PREFIX]_LT_LDFLAGS in the relevant Makefile.am's + +dnl call AM_PROG_LIBTOOL after this call + +AC_DEFUN([AS_LIBTOOL], +[ + [$1]_CURRENT=[$2] + [$1]_REVISION=[$3] + [$1]_AGE=[$4] + [$1]_LIBVERSION=[$2]:[$3]:[$4] + AC_SUBST([$1]_CURRENT) + AC_SUBST([$1]_REVISION) + AC_SUBST([$1]_AGE) + AC_SUBST([$1]_LIBVERSION) + + [$1]_LT_LDFLAGS="$[$1]_LT_LDFLAGS -version-info $[$1]_LIBVERSION" + if test ! -z "[$5]" + then + [$1]_LT_LDFLAGS="$[$1]_LT_LDFLAGS -release [$5]" + fi + AC_SUBST([$1]_LT_LDFLAGS) + + LT_PREREQ([2.2.6]) + LT_INIT([dlopen win32-dll disable-static]) +]) diff --git a/validate/common/m4/as-python.m4 b/validate/common/m4/as-python.m4 new file mode 100644 index 0000000..eb9b175 --- /dev/null +++ b/validate/common/m4/as-python.m4 @@ -0,0 +1,152 @@ +## ------------------------ +## Python file handling +## From Andrew Dalke +## Updated by James Henstridge +## Updated by Andy Wingo to loop through possible pythons +## ------------------------ + +# AS_PATH_PYTHON([MINIMUM-VERSION]) + +# Adds support for distributing Python modules and packages. To +# install modules, copy them to $(pythondir), using the python_PYTHON +# automake variable. To install a package with the same name as the +# automake package, install to $(pkgpythondir), or use the +# pkgpython_PYTHON automake variable. + +# The variables $(pyexecdir) and $(pkgpyexecdir) are provided as +# locations to install python extension modules (shared libraries). +# Another macro is required to find the appropriate flags to compile +# extension modules. + +# If your package is configured with a different prefix to python, +# users will have to add the install directory to the PYTHONPATH +# environment variable, or create a .pth file (see the python +# documentation for details). + +# If the MINIMUM-VERSION argument is passed, AS_PATH_PYTHON will +# cause an error if the version of python installed on the system +# doesn't meet the requirement. MINIMUM-VERSION should consist of +# numbers and dots only. + +# Updated to loop over all possible python binaries by Andy Wingo +# +# Updated to only warn and unset PYTHON if no good one is found + +AC_DEFUN([AS_PATH_PYTHON], + [ + dnl Find a version of Python. I could check for python versions 1.4 + dnl or earlier, but the default installation locations changed from + dnl $prefix/lib/site-python in 1.4 to $prefix/lib/python1.5/site-packages + dnl in 1.5, and I don't want to maintain that logic. + + dnl should we do the version check? + PYTHON_CANDIDATES="python python2.2 python2.1 python2.0 python2 \ + python1.6 python1.5" + ifelse([$1],[], + [AC_PATH_PROG(PYTHON, $PYTHON_CANDIDATES)], + [ + AC_MSG_NOTICE(Looking for Python version >= $1) + changequote(<<, >>)dnl + prog=" +import sys, string +minver = '$1' +# split string by '.' and convert to numeric +minver_info = map(string.atoi, string.split(minver, '.')) +# we can now do comparisons on the two lists: +if sys.version_info >= tuple(minver_info): + sys.exit(0) +else: + sys.exit(1)" + changequote([, ])dnl + + python_good=false + for python_candidate in $PYTHON_CANDIDATES; do + unset PYTHON + AC_PATH_PROG(PYTHON, $python_candidate) 1> /dev/null 2> /dev/null + + if test "x$PYTHON" = "x"; then continue; fi + + if $PYTHON -c "$prog" 1>&AC_FD_CC 2>&AC_FD_CC; then + AC_MSG_CHECKING(["$PYTHON":]) + AC_MSG_RESULT([okay]) + python_good=true + break; + else + dnl clear the cache val + unset ac_cv_path_PYTHON + fi + done + ]) + + if test "$python_good" != "true"; then + AC_MSG_WARN([No suitable version of python found]) + PYTHON= + else + + AC_MSG_CHECKING([local Python configuration]) + + dnl Query Python for its version number. Getting [:3] seems to be + dnl the best way to do this; it's what "site.py" does in the standard + dnl library. Need to change quote character because of [:3] + + AC_SUBST(PYTHON_VERSION) + changequote(<<, >>)dnl + PYTHON_VERSION=`$PYTHON -c "import sys; print sys.version[:3]"` + changequote([, ])dnl + + + dnl Use the values of $prefix and $exec_prefix for the corresponding + dnl values of PYTHON_PREFIX and PYTHON_EXEC_PREFIX. These are made + dnl distinct variables so they can be overridden if need be. However, + dnl general consensus is that you shouldn't need this ability. + + AC_SUBST(PYTHON_PREFIX) + PYTHON_PREFIX='${prefix}' + + AC_SUBST(PYTHON_EXEC_PREFIX) + PYTHON_EXEC_PREFIX='${exec_prefix}' + + dnl At times (like when building shared libraries) you may want + dnl to know which OS platform Python thinks this is. + + AC_SUBST(PYTHON_PLATFORM) + PYTHON_PLATFORM=`$PYTHON -c "import sys; print sys.platform"` + + + dnl Set up 4 directories: + + dnl pythondir -- where to install python scripts. This is the + dnl site-packages directory, not the python standard library + dnl directory like in previous automake betas. This behaviour + dnl is more consistent with lispdir.m4 for example. + dnl + dnl Also, if the package prefix isn't the same as python's prefix, + dnl then the old $(pythondir) was pretty useless. + + AC_SUBST(pythondir) + pythondir=$PYTHON_PREFIX"/lib/python"$PYTHON_VERSION/site-packages + + dnl pkgpythondir -- $PACKAGE directory under pythondir. Was + dnl PYTHON_SITE_PACKAGE in previous betas, but this naming is + dnl more consistent with the rest of automake. + dnl Maybe this should be put in python.am? + + AC_SUBST(pkgpythondir) + pkgpythondir=\${pythondir}/$PACKAGE + + dnl pyexecdir -- directory for installing python extension modules + dnl (shared libraries) Was PYTHON_SITE_EXEC in previous betas. + + AC_SUBST(pyexecdir) + pyexecdir=$PYTHON_EXEC_PREFIX"/lib/python"$PYTHON_VERSION/site-packages + + dnl pkgpyexecdir -- $(pyexecdir)/$(PACKAGE) + dnl Maybe this should be put in python.am? + + AC_SUBST(pkgpyexecdir) + pkgpyexecdir=\${pyexecdir}/$PACKAGE + + AC_MSG_RESULT([looks good]) + + fi +]) diff --git a/validate/common/m4/as-version.m4 b/validate/common/m4/as-version.m4 new file mode 100644 index 0000000..22ff774 --- /dev/null +++ b/validate/common/m4/as-version.m4 @@ -0,0 +1,75 @@ +dnl as-version.m4 0.2.0 + +dnl autostars m4 macro for versioning + +dnl Thomas Vander Stichele + +dnl $Id: as-version.m4,v 1.15 2006/04/01 09:40:24 thomasvs Exp $ + +dnl AS_VERSION + +dnl example +dnl AS_VERSION + +dnl this macro +dnl - AC_SUBST's PACKAGE_VERSION_MAJOR, _MINOR, _MICRO +dnl - AC_SUBST's PACKAGE_VERSION_RELEASE, +dnl which can be used for rpm release fields +dnl - doesn't call AM_INIT_AUTOMAKE anymore because it prevents +dnl maintainer mode from running correctly +dnl +dnl don't forget to put #undef PACKAGE_VERSION_RELEASE in acconfig.h +dnl if you use acconfig.h + +AC_DEFUN([AS_VERSION], +[ + PACKAGE_VERSION_MAJOR=$(echo AC_PACKAGE_VERSION | cut -d'.' -f1) + PACKAGE_VERSION_MINOR=$(echo AC_PACKAGE_VERSION | cut -d'.' -f2) + PACKAGE_VERSION_MICRO=$(echo AC_PACKAGE_VERSION | cut -d'.' -f3) + + AC_SUBST(PACKAGE_VERSION_MAJOR) + AC_SUBST(PACKAGE_VERSION_MINOR) + AC_SUBST(PACKAGE_VERSION_MICRO) +]) + +dnl AS_NANO(ACTION-IF-NANO-NON-NULL, [ACTION-IF-NANO-NULL]) + +dnl requires AC_INIT to be called before +dnl For projects using a fourth or nano number in your versioning to indicate +dnl development or prerelease snapshots, this macro allows the build to be +dnl set up differently accordingly. + +dnl this macro: +dnl - parses AC_PACKAGE_VERSION, set by AC_INIT, and extracts the nano number +dnl - sets the variable PACKAGE_VERSION_NANO +dnl - sets the variable PACKAGE_VERSION_RELEASE, which can be used +dnl for rpm release fields +dnl - executes ACTION-IF-NANO-NON-NULL or ACTION-IF-NANO-NULL + +dnl example: +dnl AS_NANO(RELEASE="yes", RELEASE="no") + +AC_DEFUN([AS_NANO], +[ + AC_MSG_CHECKING(nano version) + + NANO=$(echo AC_PACKAGE_VERSION | cut -d'.' -f4) + + if test x"$NANO" = x || test "x$NANO" = "x0" ; then + AC_MSG_RESULT([0 (release)]) + NANO=0 + PACKAGE_VERSION_RELEASE=1 + ifelse([$1], , :, [$1]) + else + AC_MSG_RESULT($NANO) + PACKAGE_VERSION_RELEASE=0.`date +%Y%m%d.%H%M%S` + if test "x$NANO" != "x1" ; then + ifelse([$1], , :, [$1]) + else + ifelse([$2], , :, [$2]) + fi + fi + PACKAGE_VERSION_NANO=$NANO + AC_SUBST(PACKAGE_VERSION_NANO) + AC_SUBST(PACKAGE_VERSION_RELEASE) +]) diff --git a/validate/common/m4/ax_create_stdint_h.m4 b/validate/common/m4/ax_create_stdint_h.m4 new file mode 100644 index 0000000..13bf699 --- /dev/null +++ b/validate/common/m4/ax_create_stdint_h.m4 @@ -0,0 +1,734 @@ +##### http://autoconf-archive.cryp.to/ax_create_stdint_h.html +# +# SYNOPSIS +# +# AX_CREATE_STDINT_H [( HEADER-TO-GENERATE [, HEDERS-TO-CHECK])] +# +# DESCRIPTION +# +# the "ISO C9X: 7.18 Integer types " section requires the +# existence of an include file that defines a set of +# typedefs, especially uint8_t,int32_t,uintptr_t. Many older +# installations will not provide this file, but some will have the +# very same definitions in . In other enviroments we can +# use the inet-types in which would define the typedefs +# int8_t and u_int8_t respectivly. +# +# This macros will create a local "_stdint.h" or the headerfile given +# as an argument. In many cases that file will just "#include +# " or "#include ", while in other environments +# it will provide the set of basic 'stdint's definitions/typedefs: +# +# int8_t,uint8_t,int16_t,uint16_t,int32_t,uint32_t,intptr_t,uintptr_t +# int_least32_t.. int_fast32_t.. intmax_t +# +# which may or may not rely on the definitions of other files, or +# using the AC_CHECK_SIZEOF macro to determine the actual sizeof each +# type. +# +# if your header files require the stdint-types you will want to +# create an installable file mylib-int.h that all your other +# installable header may include. So if you have a library package +# named "mylib", just use +# +# AX_CREATE_STDINT_H(mylib-int.h) +# +# in configure.ac and go to install that very header file in +# Makefile.am along with the other headers (mylib.h) - and the +# mylib-specific headers can simply use "#include " to +# obtain the stdint-types. +# +# Remember, if the system already had a valid , the +# generated file will include it directly. No need for fuzzy +# HAVE_STDINT_H things... (oops, GCC 4.2.x has deliberatly disabled +# its stdint.h for non-c99 compilation and the c99-mode is not the +# default. Therefore this macro will not use the compiler's stdint.h +# - please complain to the GCC developers). +# +# LAST MODIFICATION +# +# 2007-06-27 +# +# COPYLEFT +# +# Copyright (c) 2007 Guido U. Draheim +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. +# +# This program 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 +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +# +# As a special exception, the respective Autoconf Macro's copyright +# owner gives unlimited permission to copy, distribute and modify the +# configure scripts that are the output of Autoconf when processing +# the Macro. You need not follow the terms of the GNU General Public +# License when using or distributing such scripts, even though +# portions of the text of the Macro appear in them. The GNU General +# Public License (GPL) does govern all other use of the material that +# constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the +# Autoconf Macro released by the Autoconf Macro Archive. When you +# make and distribute a modified version of the Autoconf Macro, you +# may extend this special exception to the GPL to apply to your +# modified version as well. + +AC_DEFUN([AX_CHECK_DATA_MODEL],[ + AC_CHECK_SIZEOF(char) + AC_CHECK_SIZEOF(short) + AC_CHECK_SIZEOF(int) + AC_CHECK_SIZEOF(long) + AC_CHECK_SIZEOF(void*) + ac_cv_char_data_model="" + ac_cv_char_data_model="$ac_cv_char_data_model$ac_cv_sizeof_char" + ac_cv_char_data_model="$ac_cv_char_data_model$ac_cv_sizeof_short" + ac_cv_char_data_model="$ac_cv_char_data_model$ac_cv_sizeof_int" + ac_cv_long_data_model="" + ac_cv_long_data_model="$ac_cv_long_data_model$ac_cv_sizeof_int" + ac_cv_long_data_model="$ac_cv_long_data_model$ac_cv_sizeof_long" + ac_cv_long_data_model="$ac_cv_long_data_model$ac_cv_sizeof_voidp" + AC_MSG_CHECKING([data model]) + case "$ac_cv_char_data_model/$ac_cv_long_data_model" in + 122/242) ac_cv_data_model="IP16" ; n="standard 16bit machine" ;; + 122/244) ac_cv_data_model="LP32" ; n="standard 32bit machine" ;; + 122/*) ac_cv_data_model="i16" ; n="unusual int16 model" ;; + 124/444) ac_cv_data_model="ILP32" ; n="standard 32bit unixish" ;; + 124/488) ac_cv_data_model="LP64" ; n="standard 64bit unixish" ;; + 124/448) ac_cv_data_model="LLP64" ; n="unusual 64bit unixish" ;; + 124/*) ac_cv_data_model="i32" ; n="unusual int32 model" ;; + 128/888) ac_cv_data_model="ILP64" ; n="unusual 64bit numeric" ;; + 128/*) ac_cv_data_model="i64" ; n="unusual int64 model" ;; + 222/*2) ac_cv_data_model="DSP16" ; n="strict 16bit dsptype" ;; + 333/*3) ac_cv_data_model="DSP24" ; n="strict 24bit dsptype" ;; + 444/*4) ac_cv_data_model="DSP32" ; n="strict 32bit dsptype" ;; + 666/*6) ac_cv_data_model="DSP48" ; n="strict 48bit dsptype" ;; + 888/*8) ac_cv_data_model="DSP64" ; n="strict 64bit dsptype" ;; + 222/*|333/*|444/*|666/*|888/*) : + ac_cv_data_model="iDSP" ; n="unusual dsptype" ;; + *) ac_cv_data_model="none" ; n="very unusual model" ;; + esac + AC_MSG_RESULT([$ac_cv_data_model ($ac_cv_long_data_model, $n)]) +]) + +dnl AX_CHECK_HEADER_STDINT_X([HEADERLIST][,ACTION-IF]) +AC_DEFUN([AX_CHECK_HEADER_STDINT_X],[ +AC_CACHE_CHECK([for stdint uintptr_t], [ac_cv_header_stdint_x],[ + ac_cv_header_stdint_x="" # the 1997 typedefs (inttypes.h) + AC_MSG_RESULT([(..)]) + for i in m4_ifval([$1],[$1],[stdint.h inttypes.h sys/inttypes.h sys/types.h]) + do + unset ac_cv_type_uintptr_t + unset ac_cv_type_uint64_t + AC_CHECK_TYPE(uintptr_t,[ac_cv_header_stdint_x=$i],continue,[#include <$i>]) + AC_CHECK_TYPE(uint64_t,[and64="/uint64_t"],[and64=""],[#include<$i>]) + m4_ifvaln([$2],[$2]) break + done + AC_MSG_CHECKING([for stdint uintptr_t]) + ]) +]) + +AC_DEFUN([AX_CHECK_HEADER_STDINT_O],[ +AC_CACHE_CHECK([for stdint uint32_t], [ac_cv_header_stdint_o],[ + ac_cv_header_stdint_o="" # the 1995 typedefs (sys/inttypes.h) + AC_MSG_RESULT([(..)]) + for i in m4_ifval([$1],[$1],[inttypes.h sys/inttypes.h sys/types.h stdint.h]) + do + unset ac_cv_type_uint32_t + unset ac_cv_type_uint64_t + AC_CHECK_TYPE(uint32_t,[ac_cv_header_stdint_o=$i],continue,[#include <$i>]) + AC_CHECK_TYPE(uint64_t,[and64="/uint64_t"],[and64=""],[#include<$i>]) + m4_ifvaln([$2],[$2]) break + break; + done + AC_MSG_CHECKING([for stdint uint32_t]) + ]) +]) + +AC_DEFUN([AX_CHECK_HEADER_STDINT_U],[ +AC_CACHE_CHECK([for stdint u_int32_t], [ac_cv_header_stdint_u],[ + ac_cv_header_stdint_u="" # the BSD typedefs (sys/types.h) + AC_MSG_RESULT([(..)]) + for i in m4_ifval([$1],[$1],[sys/types.h inttypes.h sys/inttypes.h]) ; do + unset ac_cv_type_u_int32_t + unset ac_cv_type_u_int64_t + AC_CHECK_TYPE(u_int32_t,[ac_cv_header_stdint_u=$i],continue,[#include <$i>]) + AC_CHECK_TYPE(u_int64_t,[and64="/u_int64_t"],[and64=""],[#include<$i>]) + m4_ifvaln([$2],[$2]) break + break; + done + AC_MSG_CHECKING([for stdint u_int32_t]) + ]) +]) + +AC_DEFUN([AX_CREATE_STDINT_H], +[# ------ AX CREATE STDINT H ------------------------------------- +AC_MSG_CHECKING([for stdint types]) +ac_stdint_h=`echo ifelse($1, , _stdint.h, $1)` +# try to shortcircuit - if the default include path of the compiler +# can find a "stdint.h" header then we assume that all compilers can. +AC_CACHE_VAL([ac_cv_header_stdint_t],[ +old_CXXFLAGS="$CXXFLAGS" ; CXXFLAGS="" +old_CPPFLAGS="$CPPFLAGS" ; CPPFLAGS="" +old_CFLAGS="$CFLAGS" ; CFLAGS="" +AC_TRY_COMPILE([#include ],[int_least32_t v = 0;], +[ac_cv_stdint_result="(assuming C99 compatible system)" + ac_cv_header_stdint_t="stdint.h"; ], +[ac_cv_header_stdint_t=""]) +if test "$GCC" = "yes" && test ".$ac_cv_header_stdint_t" = "."; then +CFLAGS="-std=c99" +AC_TRY_COMPILE([#include ],[int_least32_t v = 0;], +[AC_MSG_WARN(your GCC compiler has a defunct stdint.h for its default-mode)]) +fi +CXXFLAGS="$old_CXXFLAGS" +CPPFLAGS="$old_CPPFLAGS" +CFLAGS="$old_CFLAGS" ]) + +v="... $ac_cv_header_stdint_h" +if test "$ac_stdint_h" = "stdint.h" ; then + AC_MSG_RESULT([(are you sure you want them in ./stdint.h?)]) +elif test "$ac_stdint_h" = "inttypes.h" ; then + AC_MSG_RESULT([(are you sure you want them in ./inttypes.h?)]) +elif test "_$ac_cv_header_stdint_t" = "_" ; then + AC_MSG_RESULT([(putting them into $ac_stdint_h)$v]) +else + ac_cv_header_stdint="$ac_cv_header_stdint_t" + AC_MSG_RESULT([$ac_cv_header_stdint (shortcircuit)]) +fi + +if test "_$ac_cv_header_stdint_t" = "_" ; then # can not shortcircuit.. + +dnl .....intro message done, now do a few system checks..... +dnl btw, all old CHECK_TYPE macros do automatically "DEFINE" a type, +dnl therefore we use the autoconf implementation detail CHECK_TYPE_NEW +dnl instead that is triggered with 3 or more arguments (see types.m4) + +inttype_headers=`echo $2 | sed -e 's/,/ /g'` + +ac_cv_stdint_result="(no helpful system typedefs seen)" +AX_CHECK_HEADER_STDINT_X(dnl + stdint.h inttypes.h sys/inttypes.h $inttype_headers, + ac_cv_stdint_result="(seen uintptr_t$and64 in $i)") + +if test "_$ac_cv_header_stdint_x" = "_" ; then +AX_CHECK_HEADER_STDINT_O(dnl, + inttypes.h sys/inttypes.h stdint.h $inttype_headers, + ac_cv_stdint_result="(seen uint32_t$and64 in $i)") +fi + +if test "_$ac_cv_header_stdint_x" = "_" ; then +if test "_$ac_cv_header_stdint_o" = "_" ; then +AX_CHECK_HEADER_STDINT_U(dnl, + sys/types.h inttypes.h sys/inttypes.h $inttype_headers, + ac_cv_stdint_result="(seen u_int32_t$and64 in $i)") +fi fi + +dnl if there was no good C99 header file, do some typedef checks... +if test "_$ac_cv_header_stdint_x" = "_" ; then + AC_MSG_CHECKING([for stdint datatype model]) + AC_MSG_RESULT([(..)]) + AX_CHECK_DATA_MODEL +fi + +if test "_$ac_cv_header_stdint_x" != "_" ; then + ac_cv_header_stdint="$ac_cv_header_stdint_x" +elif test "_$ac_cv_header_stdint_o" != "_" ; then + ac_cv_header_stdint="$ac_cv_header_stdint_o" +elif test "_$ac_cv_header_stdint_u" != "_" ; then + ac_cv_header_stdint="$ac_cv_header_stdint_u" +else + ac_cv_header_stdint="stddef.h" +fi + +AC_MSG_CHECKING([for extra inttypes in chosen header]) +AC_MSG_RESULT([($ac_cv_header_stdint)]) +dnl see if int_least and int_fast types are present in _this_ header. +unset ac_cv_type_int_least32_t +unset ac_cv_type_int_fast32_t +AC_CHECK_TYPE(int_least32_t,,,[#include <$ac_cv_header_stdint>]) +AC_CHECK_TYPE(int_fast32_t,,,[#include<$ac_cv_header_stdint>]) +AC_CHECK_TYPE(intmax_t,,,[#include <$ac_cv_header_stdint>]) + +fi # shortcircut to system "stdint.h" +# ------------------ PREPARE VARIABLES ------------------------------ +if test "$GCC" = "yes" ; then +ac_cv_stdint_message="using gnu compiler "`$CC --version | head -1` +else +ac_cv_stdint_message="using $CC" +fi + +AC_MSG_RESULT([make use of $ac_cv_header_stdint in $ac_stdint_h dnl +$ac_cv_stdint_result]) + +dnl ----------------------------------------------------------------- +# ----------------- DONE inttypes.h checks START header ------------- +AC_CONFIG_COMMANDS([$ac_stdint_h],[ +AC_MSG_NOTICE(creating $ac_stdint_h : $_ac_stdint_h) +ac_stdint=$tmp/_stdint.h + +echo "#ifndef" $_ac_stdint_h >$ac_stdint +echo "#define" $_ac_stdint_h "1" >>$ac_stdint +echo "#ifndef" _GENERATED_STDINT_H >>$ac_stdint +echo "#define" _GENERATED_STDINT_H '"'$PACKAGE $VERSION'"' >>$ac_stdint +echo "/* generated $ac_cv_stdint_message */" >>$ac_stdint +if test "_$ac_cv_header_stdint_t" != "_" ; then +echo "#define _STDINT_HAVE_STDINT_H" "1" >>$ac_stdint +echo "#include " >>$ac_stdint +echo "#endif" >>$ac_stdint +echo "#endif" >>$ac_stdint +else + +cat >>$ac_stdint < +#else +#include + +/* .................... configured part ............................ */ + +STDINT_EOF + +echo "/* whether we have a C99 compatible stdint header file */" >>$ac_stdint +if test "_$ac_cv_header_stdint_x" != "_" ; then + ac_header="$ac_cv_header_stdint_x" + echo "#define _STDINT_HEADER_INTPTR" '"'"$ac_header"'"' >>$ac_stdint +else + echo "/* #undef _STDINT_HEADER_INTPTR */" >>$ac_stdint +fi + +echo "/* whether we have a C96 compatible inttypes header file */" >>$ac_stdint +if test "_$ac_cv_header_stdint_o" != "_" ; then + ac_header="$ac_cv_header_stdint_o" + echo "#define _STDINT_HEADER_UINT32" '"'"$ac_header"'"' >>$ac_stdint +else + echo "/* #undef _STDINT_HEADER_UINT32 */" >>$ac_stdint +fi + +echo "/* whether we have a BSD compatible inet types header */" >>$ac_stdint +if test "_$ac_cv_header_stdint_u" != "_" ; then + ac_header="$ac_cv_header_stdint_u" + echo "#define _STDINT_HEADER_U_INT32" '"'"$ac_header"'"' >>$ac_stdint +else + echo "/* #undef _STDINT_HEADER_U_INT32 */" >>$ac_stdint +fi + +echo "" >>$ac_stdint + +if test "_$ac_header" != "_" ; then if test "$ac_header" != "stddef.h" ; then + echo "#include <$ac_header>" >>$ac_stdint + echo "" >>$ac_stdint +fi fi + +echo "/* which 64bit typedef has been found */" >>$ac_stdint +if test "$ac_cv_type_uint64_t" = "yes" ; then +echo "#define _STDINT_HAVE_UINT64_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_UINT64_T */" >>$ac_stdint +fi +if test "$ac_cv_type_u_int64_t" = "yes" ; then +echo "#define _STDINT_HAVE_U_INT64_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_U_INT64_T */" >>$ac_stdint +fi +echo "" >>$ac_stdint + +echo "/* which type model has been detected */" >>$ac_stdint +if test "_$ac_cv_char_data_model" != "_" ; then +echo "#define _STDINT_CHAR_MODEL" "$ac_cv_char_data_model" >>$ac_stdint +echo "#define _STDINT_LONG_MODEL" "$ac_cv_long_data_model" >>$ac_stdint +else +echo "/* #undef _STDINT_CHAR_MODEL // skipped */" >>$ac_stdint +echo "/* #undef _STDINT_LONG_MODEL // skipped */" >>$ac_stdint +fi +echo "" >>$ac_stdint + +echo "/* whether int_least types were detected */" >>$ac_stdint +if test "$ac_cv_type_int_least32_t" = "yes"; then +echo "#define _STDINT_HAVE_INT_LEAST32_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_INT_LEAST32_T */" >>$ac_stdint +fi +echo "/* whether int_fast types were detected */" >>$ac_stdint +if test "$ac_cv_type_int_fast32_t" = "yes"; then +echo "#define _STDINT_HAVE_INT_FAST32_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_INT_FAST32_T */" >>$ac_stdint +fi +echo "/* whether intmax_t type was detected */" >>$ac_stdint +if test "$ac_cv_type_intmax_t" = "yes"; then +echo "#define _STDINT_HAVE_INTMAX_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_INTMAX_T */" >>$ac_stdint +fi +echo "" >>$ac_stdint + + cat >>$ac_stdint <= 199901L +#define _HAVE_UINT64_T +#define _HAVE_LONGLONG_UINT64_T +typedef long long int64_t; +typedef unsigned long long uint64_t; + +#elif !defined __STRICT_ANSI__ +#if defined _MSC_VER || defined __WATCOMC__ || defined __BORLANDC__ +#define _HAVE_UINT64_T +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; + +#elif defined __GNUC__ || defined __MWERKS__ || defined __ELF__ +/* note: all ELF-systems seem to have loff-support which needs 64-bit */ +#if !defined _NO_LONGLONG +#define _HAVE_UINT64_T +#define _HAVE_LONGLONG_UINT64_T +typedef long long int64_t; +typedef unsigned long long uint64_t; +#endif + +#elif defined __alpha || (defined __mips && defined _ABIN32) +#if !defined _NO_LONGLONG +typedef long int64_t; +typedef unsigned long uint64_t; +#endif + /* compiler/cpu type to define int64_t */ +#endif +#endif +#endif + +#if defined _STDINT_HAVE_U_INT_TYPES +/* int8_t int16_t int32_t defined by inet code, redeclare the u_intXX types */ +typedef u_int8_t uint8_t; +typedef u_int16_t uint16_t; +typedef u_int32_t uint32_t; + +/* glibc compatibility */ +#ifndef __int8_t_defined +#define __int8_t_defined +#endif +#endif + +#ifdef _STDINT_NEED_INT_MODEL_T +/* we must guess all the basic types. Apart from byte-adressable system, */ +/* there a few 32-bit-only dsp-systems that we guard with BYTE_MODEL 8-} */ +/* (btw, those nibble-addressable systems are way off, or so we assume) */ + +dnl /* have a look at "64bit and data size neutrality" at */ +dnl /* http://unix.org/version2/whatsnew/login_64bit.html */ +dnl /* (the shorthand "ILP" types always have a "P" part) */ + +#if defined _STDINT_BYTE_MODEL +#if _STDINT_LONG_MODEL+0 == 242 +/* 2:4:2 = IP16 = a normal 16-bit system */ +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned long uint32_t; +#ifndef __int8_t_defined +#define __int8_t_defined +typedef char int8_t; +typedef short int16_t; +typedef long int32_t; +#endif +#elif _STDINT_LONG_MODEL+0 == 244 || _STDINT_LONG_MODEL == 444 +/* 2:4:4 = LP32 = a 32-bit system derived from a 16-bit */ +/* 4:4:4 = ILP32 = a normal 32-bit system */ +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +#ifndef __int8_t_defined +#define __int8_t_defined +typedef char int8_t; +typedef short int16_t; +typedef int int32_t; +#endif +#elif _STDINT_LONG_MODEL+0 == 484 || _STDINT_LONG_MODEL+0 == 488 +/* 4:8:4 = IP32 = a 32-bit system prepared for 64-bit */ +/* 4:8:8 = LP64 = a normal 64-bit system */ +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +#ifndef __int8_t_defined +#define __int8_t_defined +typedef char int8_t; +typedef short int16_t; +typedef int int32_t; +#endif +/* this system has a "long" of 64bit */ +#ifndef _HAVE_UINT64_T +#define _HAVE_UINT64_T +typedef unsigned long uint64_t; +typedef long int64_t; +#endif +#elif _STDINT_LONG_MODEL+0 == 448 +/* LLP64 a 64-bit system derived from a 32-bit system */ +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +#ifndef __int8_t_defined +#define __int8_t_defined +typedef char int8_t; +typedef short int16_t; +typedef int int32_t; +#endif +/* assuming the system has a "long long" */ +#ifndef _HAVE_UINT64_T +#define _HAVE_UINT64_T +#define _HAVE_LONGLONG_UINT64_T +typedef unsigned long long uint64_t; +typedef long long int64_t; +#endif +#else +#define _STDINT_NO_INT32_T +#endif +#else +#define _STDINT_NO_INT8_T +#define _STDINT_NO_INT32_T +#endif +#endif + +/* + * quote from SunOS-5.8 sys/inttypes.h: + * Use at your own risk. As of February 1996, the committee is squarely + * behind the fixed sized types; the "least" and "fast" types are still being + * discussed. The probability that the "fast" types may be removed before + * the standard is finalized is high enough that they are not currently + * implemented. + */ + +#if defined _STDINT_NEED_INT_LEAST_T +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +#ifdef _HAVE_UINT64_T +typedef int64_t int_least64_t; +#endif + +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +#ifdef _HAVE_UINT64_T +typedef uint64_t uint_least64_t; +#endif + /* least types */ +#endif + +#if defined _STDINT_NEED_INT_FAST_T +typedef int8_t int_fast8_t; +typedef int int_fast16_t; +typedef int32_t int_fast32_t; +#ifdef _HAVE_UINT64_T +typedef int64_t int_fast64_t; +#endif + +typedef uint8_t uint_fast8_t; +typedef unsigned uint_fast16_t; +typedef uint32_t uint_fast32_t; +#ifdef _HAVE_UINT64_T +typedef uint64_t uint_fast64_t; +#endif + /* fast types */ +#endif + +#ifdef _STDINT_NEED_INTMAX_T +#ifdef _HAVE_UINT64_T +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; +#else +typedef long intmax_t; +typedef unsigned long uintmax_t; +#endif +#endif + +#ifdef _STDINT_NEED_INTPTR_T +#ifndef __intptr_t_defined +#define __intptr_t_defined +/* we encourage using "long" to store pointer values, never use "int" ! */ +#if _STDINT_LONG_MODEL+0 == 242 || _STDINT_LONG_MODEL+0 == 484 +typedef unsigned int uintptr_t; +typedef int intptr_t; +#elif _STDINT_LONG_MODEL+0 == 244 || _STDINT_LONG_MODEL+0 == 444 +typedef unsigned long uintptr_t; +typedef long intptr_t; +#elif _STDINT_LONG_MODEL+0 == 448 && defined _HAVE_UINT64_T +typedef uint64_t uintptr_t; +typedef int64_t intptr_t; +#else /* matches typical system types ILP32 and LP64 - but not IP16 or LLP64 */ +typedef unsigned long uintptr_t; +typedef long intptr_t; +#endif +#endif +#endif + +/* The ISO C99 standard specifies that in C++ implementations these + should only be defined if explicitly requested. */ +#if !defined __cplusplus || defined __STDC_CONSTANT_MACROS +#ifndef UINT32_C + +/* Signed. */ +# define INT8_C(c) c +# define INT16_C(c) c +# define INT32_C(c) c +# ifdef _HAVE_LONGLONG_UINT64_T +# define INT64_C(c) c ## L +# else +# define INT64_C(c) c ## LL +# endif + +/* Unsigned. */ +# define UINT8_C(c) c ## U +# define UINT16_C(c) c ## U +# define UINT32_C(c) c ## U +# ifdef _HAVE_LONGLONG_UINT64_T +# define UINT64_C(c) c ## UL +# else +# define UINT64_C(c) c ## ULL +# endif + +/* Maximal type. */ +# ifdef _HAVE_LONGLONG_UINT64_T +# define INTMAX_C(c) c ## L +# define UINTMAX_C(c) c ## UL +# else +# define INTMAX_C(c) c ## LL +# define UINTMAX_C(c) c ## ULL +# endif + + /* literalnumbers */ +#endif +#endif + +/* These limits are merily those of a two complement byte-oriented system */ + +/* Minimum of signed integral types. */ +# define INT8_MIN (-128) +# define INT16_MIN (-32767-1) +# define INT32_MIN (-2147483647-1) +# define INT64_MIN (-__INT64_C(9223372036854775807)-1) +/* Maximum of signed integral types. */ +# define INT8_MAX (127) +# define INT16_MAX (32767) +# define INT32_MAX (2147483647) +# define INT64_MAX (__INT64_C(9223372036854775807)) + +/* Maximum of unsigned integral types. */ +# define UINT8_MAX (255) +# define UINT16_MAX (65535) +# define UINT32_MAX (4294967295U) +# define UINT64_MAX (__UINT64_C(18446744073709551615)) + +/* Minimum of signed integral types having a minimum size. */ +# define INT_LEAST8_MIN INT8_MIN +# define INT_LEAST16_MIN INT16_MIN +# define INT_LEAST32_MIN INT32_MIN +# define INT_LEAST64_MIN INT64_MIN +/* Maximum of signed integral types having a minimum size. */ +# define INT_LEAST8_MAX INT8_MAX +# define INT_LEAST16_MAX INT16_MAX +# define INT_LEAST32_MAX INT32_MAX +# define INT_LEAST64_MAX INT64_MAX + +/* Maximum of unsigned integral types having a minimum size. */ +# define UINT_LEAST8_MAX UINT8_MAX +# define UINT_LEAST16_MAX UINT16_MAX +# define UINT_LEAST32_MAX UINT32_MAX +# define UINT_LEAST64_MAX UINT64_MAX + + /* shortcircuit*/ +#endif + /* once */ +#endif +#endif +STDINT_EOF +fi + if cmp -s $ac_stdint_h $ac_stdint 2>/dev/null; then + AC_MSG_NOTICE([$ac_stdint_h is unchanged]) + else + ac_dir=`AS_DIRNAME(["$ac_stdint_h"])` + AS_MKDIR_P(["$ac_dir"]) + rm -f $ac_stdint_h + mv $ac_stdint $ac_stdint_h + fi +],[# variables for create stdint.h replacement +PACKAGE="$PACKAGE" +VERSION="$VERSION" +ac_stdint_h="$ac_stdint_h" +_ac_stdint_h=AS_TR_CPP(_$PACKAGE-$ac_stdint_h) +ac_cv_stdint_message="$ac_cv_stdint_message" +ac_cv_header_stdint_t="$ac_cv_header_stdint_t" +ac_cv_header_stdint_x="$ac_cv_header_stdint_x" +ac_cv_header_stdint_o="$ac_cv_header_stdint_o" +ac_cv_header_stdint_u="$ac_cv_header_stdint_u" +ac_cv_type_uint64_t="$ac_cv_type_uint64_t" +ac_cv_type_u_int64_t="$ac_cv_type_u_int64_t" +ac_cv_char_data_model="$ac_cv_char_data_model" +ac_cv_long_data_model="$ac_cv_long_data_model" +ac_cv_type_int_least32_t="$ac_cv_type_int_least32_t" +ac_cv_type_int_fast32_t="$ac_cv_type_int_fast32_t" +ac_cv_type_intmax_t="$ac_cv_type_intmax_t" +]) +]) diff --git a/validate/common/m4/ax_pthread.m4 b/validate/common/m4/ax_pthread.m4 new file mode 100644 index 0000000..d383ad5 --- /dev/null +++ b/validate/common/m4/ax_pthread.m4 @@ -0,0 +1,332 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_pthread.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +# +# DESCRIPTION +# +# This macro figures out how to build C programs using POSIX threads. It +# sets the PTHREAD_LIBS output variable to the threads library and linker +# flags, and the PTHREAD_CFLAGS output variable to any special C compiler +# flags that are needed. (The user can also force certain compiler +# flags/libs to be tested by setting these environment variables.) +# +# Also sets PTHREAD_CC to any special C compiler that is needed for +# multi-threaded programs (defaults to the value of CC otherwise). (This +# is necessary on AIX to use the special cc_r compiler alias.) +# +# NOTE: You are assumed to not only compile your program with these flags, +# but also link it with them as well. e.g. you should link with +# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS +# +# If you are only building threads programs, you may wish to use these +# variables in your default LIBS, CFLAGS, and CC: +# +# LIBS="$PTHREAD_LIBS $LIBS" +# CFLAGS="$CFLAGS $PTHREAD_CFLAGS" +# CC="$PTHREAD_CC" +# +# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant +# has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name +# (e.g. PTHREAD_CREATE_UNDETACHED on AIX). +# +# Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the +# PTHREAD_PRIO_INHERIT symbol is defined when compiling with +# PTHREAD_CFLAGS. +# +# ACTION-IF-FOUND is a list of shell commands to run if a threads library +# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it +# is not found. If ACTION-IF-FOUND is not specified, the default action +# will define HAVE_PTHREAD. +# +# Please let the authors know if this macro fails on any platform, or if +# you have any other suggestions or comments. This macro was based on work +# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help +# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by +# Alejandro Forero Cuervo to the autoconf macro repository. We are also +# grateful for the helpful feedback of numerous users. +# +# Updated for Autoconf 2.68 by Daniel Richard G. +# +# LICENSE +# +# Copyright (c) 2008 Steven G. Johnson +# Copyright (c) 2011 Daniel Richard G. +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program 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 General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 21 + +AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) +AC_DEFUN([AX_PTHREAD], [ +AC_REQUIRE([AC_CANONICAL_HOST]) +AC_LANG_PUSH([C]) +ax_pthread_ok=no + +# We used to check for pthread.h first, but this fails if pthread.h +# requires special compiler flags (e.g. on True64 or Sequent). +# It gets checked for in the link test anyway. + +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works using +# them: +if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) + AC_TRY_LINK_FUNC([pthread_join], [ax_pthread_ok=yes]) + AC_MSG_RESULT([$ax_pthread_ok]) + if test x"$ax_pthread_ok" = xno; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" +fi + +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). + +# Create a list of thread flags to try. Items starting with a "-" are +# C compiler flags, and other items are library names, except for "none" +# which indicates that we try without any flags at all, and "pthread-config" +# which is a program returning the flags for the Pth emulation library. + +ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" + +# The ordering *is* (sometimes) important. Some notes on the +# individual items follow: + +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) +# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) +# -pthreads: Solaris/gcc +# -mthreads: Mingw32/gcc, Lynx/gcc +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads too; +# also defines -D_REENTRANT) +# ... -mt is also the pthreads flag for HP/aCC +# pthread: Linux, etcetera +# --thread-safe: KAI C++ +# pthread-config: use pthread-config program (for GNU Pth library) + +case ${host_os} in + solaris*) + + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (We need to link with -pthreads/-mt/ + # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather + # a function called by this macro, so we could check for that, but + # who knows whether they'll stub that too in a future libc.) So, + # we'll just look for -pthreads and -lpthread first: + + ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags" + ;; + + darwin*) + ax_pthread_flags="-pthread $ax_pthread_flags" + ;; +esac + +# Clang doesn't consider unrecognized options an error unless we specify +# -Werror. We throw in some extra Clang-specific options to ensure that +# this doesn't happen for GCC, which also accepts -Werror. + +AC_MSG_CHECKING([if compiler needs -Werror to reject unknown flags]) +save_CFLAGS="$CFLAGS" +ax_pthread_extra_flags="-Werror" +CFLAGS="$CFLAGS $ax_pthread_extra_flags -Wunknown-warning-option -Wsizeof-array-argument" +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([int foo(void);],[foo()])], + [AC_MSG_RESULT([yes])], + [ax_pthread_extra_flags= + AC_MSG_RESULT([no])]) +CFLAGS="$save_CFLAGS" + +if test x"$ax_pthread_ok" = xno; then +for flag in $ax_pthread_flags; do + + case $flag in + none) + AC_MSG_CHECKING([whether pthreads work without any flags]) + ;; + + -*) + AC_MSG_CHECKING([whether pthreads work with $flag]) + PTHREAD_CFLAGS="$flag" + ;; + + pthread-config) + AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no]) + if test x"$ax_pthread_config" = xno; then continue; fi + PTHREAD_CFLAGS="`pthread-config --cflags`" + PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" + ;; + + *) + AC_MSG_CHECKING([for the pthreads library -l$flag]) + PTHREAD_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + save_CFLAGS="$CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS $ax_pthread_extra_flags" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include + static void routine(void *a) { a = 0; } + static void *start_routine(void *a) { return a; }], + [pthread_t th; pthread_attr_t attr; + pthread_create(&th, 0, start_routine, 0); + pthread_join(th, 0); + pthread_attr_init(&attr); + pthread_cleanup_push(routine, 0); + pthread_cleanup_pop(0) /* ; */])], + [ax_pthread_ok=yes], + []) + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + AC_MSG_RESULT([$ax_pthread_ok]) + if test "x$ax_pthread_ok" = xyes; then + break; + fi + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi + +# Various other checks: +if test "x$ax_pthread_ok" = xyes; then + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. + AC_MSG_CHECKING([for joinable pthread attribute]) + attr_name=unknown + for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], + [int attr = $attr; return attr /* ; */])], + [attr_name=$attr; break], + []) + done + AC_MSG_RESULT([$attr_name]) + if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then + AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE], [$attr_name], + [Define to necessary symbol if this constant + uses a non-standard name on your system.]) + fi + + AC_MSG_CHECKING([if more special flags are required for pthreads]) + flag=no + case ${host_os} in + aix* | freebsd* | darwin*) flag="-D_THREAD_SAFE";; + osf* | hpux*) flag="-D_REENTRANT";; + solaris*) + if test "$GCC" = "yes"; then + flag="-D_REENTRANT" + else + # TODO: What about Clang on Solaris? + flag="-mt -D_REENTRANT" + fi + ;; + esac + AC_MSG_RESULT([$flag]) + if test "x$flag" != xno; then + PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" + fi + + AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], + [ax_cv_PTHREAD_PRIO_INHERIT], [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], + [[int i = PTHREAD_PRIO_INHERIT;]])], + [ax_cv_PTHREAD_PRIO_INHERIT=yes], + [ax_cv_PTHREAD_PRIO_INHERIT=no]) + ]) + AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"], + [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.])]) + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + # More AIX lossage: compile with *_r variant + if test "x$GCC" != xyes; then + case $host_os in + aix*) + AS_CASE(["x/$CC"], + [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6], + [#handle absolute path differently from PATH based program lookup + AS_CASE(["x$CC"], + [x/*], + [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])], + [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])]) + ;; + esac + fi +fi + +test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" + +AC_SUBST([PTHREAD_LIBS]) +AC_SUBST([PTHREAD_CFLAGS]) +AC_SUBST([PTHREAD_CC]) + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test x"$ax_pthread_ok" = xyes; then + ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1]) + : +else + ax_pthread_ok=no + $2 +fi +AC_LANG_POP +])dnl AX_PTHREAD diff --git a/validate/common/m4/check.m4 b/validate/common/m4/check.m4 new file mode 100644 index 0000000..afd26eb --- /dev/null +++ b/validate/common/m4/check.m4 @@ -0,0 +1,181 @@ +dnl _AM_TRY_CHECK(MINIMUM-VERSION, EXTRA-CFLAGS, EXTRA-LIBS, CHECK-LIB-NAME +dnl [, ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]) +dnl Test for check, and define CHECK_CFLAGS and CHECK_LIBS +dnl Done this way because of the brokenness that is +dnl https://launchpad.net/distros/ubuntu/+source/check/+bug/5840 +dnl + +AC_DEFUN([_AM_TRY_CHECK], +[ + min_check_version=$1 + extra_cflags=$2 + extra_libs=$3 + check_lib_name=$4 + + CHECK_CFLAGS="$extra_cflags" + CHECK_LIBS="$extra_libs -l$check_lib_name" + + ac_save_CFLAGS="$CFLAGS" + ac_save_LIBS="$LIBS" + + CFLAGS="$CFLAGS $CHECK_CFLAGS" + LIBS="$CHECK_LIBS $LIBS" + + AC_MSG_CHECKING(for check named $check_lib_name - version >= $min_check_version) + + rm -f conf.check-test + dnl unset no_check, since in our second run it would have been set to yes + dnl before + no_check= + AC_TRY_RUN([ +#include +#include + +#include + +int main () +{ + int major, minor, micro; + char *tmp_version; + + system ("touch conf.check-test"); + + /* HP/UX 9 (%@#!) writes to sscanf strings */ + tmp_version = strdup("$min_check_version"); + if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) { + printf("%s, bad version string\n", "$min_check_version"); + return 1; + } + + if ((CHECK_MAJOR_VERSION != check_major_version) || + (CHECK_MINOR_VERSION != check_minor_version) || + (CHECK_MICRO_VERSION != check_micro_version)) + { + printf("\n*** The check header file (version %d.%d.%d) does not match\n", + CHECK_MAJOR_VERSION, CHECK_MINOR_VERSION, CHECK_MICRO_VERSION); + printf("*** the check library (version %d.%d.%d).\n", + check_major_version, check_minor_version, check_micro_version); + return 1; + } + + if ((check_major_version > major) || + ((check_major_version == major) && (check_minor_version > minor)) || + ((check_major_version == major) && (check_minor_version == minor) && (check_micro_version >= micro))) + { + return 0; + } + else + { + printf("\n*** An old version of check (%d.%d.%d) was found.\n", + check_major_version, check_minor_version, check_micro_version); + printf("*** You need a version of check being at least %d.%d.%d.\n", major, minor, micro); + printf("***\n"); + printf("*** If you have already installed a sufficiently new version, this error\n"); + printf("*** probably means that the wrong copy of the check library and header\n"); + printf("*** file is being found. Rerun configure with the --with-check=PATH option\n"); + printf("*** to specify the prefix where the correct version was installed.\n"); + } + + return 1; +} +],, no_check=yes, [echo $ac_n "cross compiling; assumed OK... $ac_c"]) + + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + + if test "x$no_check" = x ; then + AC_MSG_RESULT(yes) + ifelse([$5], , :, [$5]) + else + AC_MSG_RESULT(no) + if test -f conf.check-test ; then + : + else + echo "*** Could not run check test program, checking why..." + CFLAGS="$CFLAGS $CHECK_CFLAGS" + LIBS="$CHECK_LIBS $LIBS" + AC_TRY_LINK([ +#include +#include + +#include +], , [ echo "*** The test program compiled, but did not run. This usually means" + echo "*** that the run-time linker is not finding check. You'll need to set your" + echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" + echo "*** to the installed location Also, make sure you have run ldconfig if that" + echo "*** is required on your system" + echo "***" + echo "*** If you have an old version installed, it is best to remove it, although" + echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"], + [ echo "*** The test program failed to compile or link. See the file config.log for" + echo "*** the exact error that occured." ]) + + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + fi + + CHECK_CFLAGS="" + CHECK_LIBS="" + + rm -f conf.check-test + ifelse([$6], , AC_MSG_ERROR([check not found]), [$6]) + fi +]) + + +dnl AM_PATH_CHECK([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) +dnl Test for check, and define CHECK_CFLAGS and CHECK_LIBS +dnl + +AC_DEFUN([AM_PATH_CHECK], +[ + AC_ARG_WITH(check, + [ --with-check=PATH prefix where check is installed [default=auto]]) + + AC_ARG_WITH(checklibname, + AC_HELP_STRING([--with-check-lib-name=NAME], + [name of the PIC check library (default=check)])) + + min_check_version=ifelse([$1], ,0.8.2,$1) + + if test x$with_check = xno; then + AC_MSG_RESULT(disabled) + ifelse([$3], , AC_MSG_ERROR([disabling check is not supported]), [$3]) + else + if test "x$with_check" != x; then + CHECK_EXTRA_CFLAGS="-I$with_check/include" + CHECK_EXTRA_LIBS="-L$with_check/lib" + else + CHECK_EXTRA_CFLAGS="" + CHECK_EXTRA_LIBS="" + fi + + if test x$with_checklibname = x; then + _AM_TRY_CHECK($min_check_version, $CHECK_EXTRA_CFLAGS, $CHECK_EXTRA_LIBS, + check_pic, [have_check=true], [have_check=false]) + if test x$have_check = xtrue; then + ifelse([$2], , :, [$2]) + else + _AM_TRY_CHECK($min_check_version, $CHECK_EXTRA_CFLAGS, $CHECK_EXTRA_LIBS, + check, [have_check=true], [have_check=false]) + if test x$have_check = xtrue; then + ifelse([$2], , :, [$2]) + else + ifelse([$3], , AC_MSG_ERROR([check not found]), [$3]) + fi + fi + else + _AM_TRY_CHECK($min_check_version, $CHECK_EXTRA_CFLAGS, $CHECK_EXTRA_LIBS, + $with_checklibname, [have_check=true], [have_check=false]) + if test x$have_check = xtrue; then + ifelse([$2], , :, [$2]) + else + ifelse([$3], , AC_MSG_ERROR([check not found]), [$3]) + fi + fi + + AC_SUBST(CHECK_CFLAGS) + AC_SUBST(CHECK_LIBS) + rm -f conf.check-test + fi +]) diff --git a/validate/common/m4/glib-gettext.m4 b/validate/common/m4/glib-gettext.m4 new file mode 100644 index 0000000..f8d442f --- /dev/null +++ b/validate/common/m4/glib-gettext.m4 @@ -0,0 +1,432 @@ +# Copyright (C) 1995-2002 Free Software Foundation, Inc. +# Copyright (C) 2001-2003,2004 Red Hat, Inc. +# +# This file is free software, distributed under the terms of the GNU +# General Public License. As a special exception to the GNU General +# Public License, this file may be distributed as part of a program +# that contains a configuration script generated by Autoconf, under +# the same distribution terms as the rest of that program. +# +# This file can be copied and used freely without restrictions. It can +# be used in projects which are not available under the GNU Public License +# but which still want to provide support for the GNU gettext functionality. +# +# Macro to add for using GNU gettext. +# Ulrich Drepper , 1995, 1996 +# +# Modified to never use included libintl. +# Owen Taylor , 12/15/1998 +# +# Major rework to remove unused code +# Owen Taylor , 12/11/2002 +# +# Added better handling of ALL_LINGUAS from GNU gettext version +# written by Bruno Haible, Owen Taylor 5/30/3002 +# +# Modified to require ngettext +# Matthias Clasen 08/06/2004 +# +# We need this here as well, since someone might use autoconf-2.5x +# to configure GLib then an older version to configure a package +# using AM_GLIB_GNU_GETTEXT +AC_PREREQ(2.53) + +dnl +dnl We go to great lengths to make sure that aclocal won't +dnl try to pull in the installed version of these macros +dnl when running aclocal in the glib directory. +dnl +m4_copy([AC_DEFUN],[glib_DEFUN]) +m4_copy([AC_REQUIRE],[glib_REQUIRE]) +dnl +dnl At the end, if we're not within glib, we'll define the public +dnl definitions in terms of our private definitions. +dnl + +# GLIB_LC_MESSAGES +#-------------------- +glib_DEFUN([GLIB_LC_MESSAGES], + [AC_CHECK_HEADERS([locale.h]) + if test $ac_cv_header_locale_h = yes; then + AC_CACHE_CHECK([for LC_MESSAGES], am_cv_val_LC_MESSAGES, + [AC_TRY_LINK([#include ], [return LC_MESSAGES], + am_cv_val_LC_MESSAGES=yes, am_cv_val_LC_MESSAGES=no)]) + if test $am_cv_val_LC_MESSAGES = yes; then + AC_DEFINE(HAVE_LC_MESSAGES, 1, + [Define if your file defines LC_MESSAGES.]) + fi + fi]) + +# GLIB_PATH_PROG_WITH_TEST +#---------------------------- +dnl GLIB_PATH_PROG_WITH_TEST(VARIABLE, PROG-TO-CHECK-FOR, +dnl TEST-PERFORMED-ON-FOUND_PROGRAM [, VALUE-IF-NOT-FOUND [, PATH]]) +glib_DEFUN([GLIB_PATH_PROG_WITH_TEST], +[# Extract the first word of "$2", so it can be a program name with args. +set dummy $2; ac_word=[$]2 +AC_MSG_CHECKING([for $ac_word]) +AC_CACHE_VAL(ac_cv_path_$1, +[case "[$]$1" in + /*) + ac_cv_path_$1="[$]$1" # Let the user override the test with a path. + ;; + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in ifelse([$5], , $PATH, [$5]); do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + if [$3]; then + ac_cv_path_$1="$ac_dir/$ac_word" + break + fi + fi + done + IFS="$ac_save_ifs" +dnl If no 4th arg is given, leave the cache variable unset, +dnl so AC_PATH_PROGS will keep looking. +ifelse([$4], , , [ test -z "[$]ac_cv_path_$1" && ac_cv_path_$1="$4" +])dnl + ;; +esac])dnl +$1="$ac_cv_path_$1" +if test ifelse([$4], , [-n "[$]$1"], ["[$]$1" != "$4"]); then + AC_MSG_RESULT([$]$1) +else + AC_MSG_RESULT(no) +fi +AC_SUBST($1)dnl +]) + +# GLIB_WITH_NLS +#----------------- +glib_DEFUN([GLIB_WITH_NLS], + dnl NLS is obligatory + [USE_NLS=yes + AC_SUBST(USE_NLS) + + gt_cv_have_gettext=no + + CATOBJEXT=NONE + XGETTEXT=: + INTLLIBS= + + AC_CHECK_HEADER(libintl.h, + [gt_cv_func_dgettext_libintl="no" + libintl_extra_libs="" + + # + # First check in libc + # + AC_CACHE_CHECK([for ngettext in libc], gt_cv_func_ngettext_libc, + [AC_TRY_LINK([ +#include +], + [return !ngettext ("","", 1)], + gt_cv_func_ngettext_libc=yes, + gt_cv_func_ngettext_libc=no) + ]) + + if test "$gt_cv_func_ngettext_libc" = "yes" ; then + AC_CACHE_CHECK([for dgettext in libc], gt_cv_func_dgettext_libc, + [AC_TRY_LINK([ +#include +], + [return !dgettext ("","")], + gt_cv_func_dgettext_libc=yes, + gt_cv_func_dgettext_libc=no) + ]) + fi + + if test "$gt_cv_func_ngettext_libc" = "yes" ; then + AC_CHECK_FUNCS(bind_textdomain_codeset) + fi + + # + # If we don't have everything we want, check in libintl + # + if test "$gt_cv_func_dgettext_libc" != "yes" \ + || test "$gt_cv_func_ngettext_libc" != "yes" \ + || test "$ac_cv_func_bind_textdomain_codeset" != "yes" ; then + + AC_CHECK_LIB(intl, bindtextdomain, + [AC_CHECK_LIB(intl, ngettext, + [AC_CHECK_LIB(intl, dgettext, + gt_cv_func_dgettext_libintl=yes)])]) + + if test "$gt_cv_func_dgettext_libintl" != "yes" ; then + AC_MSG_CHECKING([if -liconv is needed to use gettext]) + AC_MSG_RESULT([]) + AC_CHECK_LIB(intl, ngettext, + [AC_CHECK_LIB(intl, dcgettext, + [gt_cv_func_dgettext_libintl=yes + libintl_extra_libs=-liconv], + :,-liconv)], + :,-liconv) + fi + + # + # If we found libintl, then check in it for bind_textdomain_codeset(); + # we'll prefer libc if neither have bind_textdomain_codeset(), + # and both have dgettext and ngettext + # + if test "$gt_cv_func_dgettext_libintl" = "yes" ; then + glib_save_LIBS="$LIBS" + LIBS="$LIBS -lintl $libintl_extra_libs" + unset ac_cv_func_bind_textdomain_codeset + AC_CHECK_FUNCS(bind_textdomain_codeset) + LIBS="$glib_save_LIBS" + + if test "$ac_cv_func_bind_textdomain_codeset" = "yes" ; then + gt_cv_func_dgettext_libc=no + else + if test "$gt_cv_func_dgettext_libc" = "yes" \ + && test "$gt_cv_func_ngettext_libc" = "yes"; then + gt_cv_func_dgettext_libintl=no + fi + fi + fi + fi + + if test "$gt_cv_func_dgettext_libc" = "yes" \ + || test "$gt_cv_func_dgettext_libintl" = "yes"; then + gt_cv_have_gettext=yes + fi + + if test "$gt_cv_func_dgettext_libintl" = "yes"; then + INTLLIBS="-lintl $libintl_extra_libs" + fi + + if test "$gt_cv_have_gettext" = "yes"; then + AC_DEFINE(HAVE_GETTEXT,1, + [Define if the GNU gettext() function is already present or preinstalled.]) + GLIB_PATH_PROG_WITH_TEST(MSGFMT, msgfmt, + [test -z "`$ac_dir/$ac_word -h 2>&1 | grep 'dv '`"], no)dnl + if test "$MSGFMT" != "no"; then + glib_save_LIBS="$LIBS" + LIBS="$LIBS $INTLLIBS" + AC_CHECK_FUNCS(dcgettext) + MSGFMT_OPTS= + AC_MSG_CHECKING([if msgfmt accepts -c]) + GLIB_RUN_PROG([$MSGFMT -c -o /dev/null],[ +msgid "" +msgstr "" +"Content-Type: text/plain; charset=UTF-8\n" +"Project-Id-Version: test 1.0\n" +"PO-Revision-Date: 2007-02-15 12:01+0100\n" +"Last-Translator: test \n" +"Language-Team: C \n" +"MIME-Version: 1.0\n" +"Content-Transfer-Encoding: 8bit\n" +], [MSGFMT_OPTS=-c; AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no])]) + AC_SUBST(MSGFMT_OPTS) + AC_PATH_PROG(GMSGFMT, gmsgfmt, $MSGFMT) + GLIB_PATH_PROG_WITH_TEST(XGETTEXT, xgettext, + [test -z "`$ac_dir/$ac_word -h 2>&1 | grep '(HELP)'`"], :) + AC_TRY_LINK(, [extern int _nl_msg_cat_cntr; + return _nl_msg_cat_cntr], + [CATOBJEXT=.gmo + DATADIRNAME=share], + [case $host in + *-*-solaris*) + dnl On Solaris, if bind_textdomain_codeset is in libc, + dnl GNU format message catalog is always supported, + dnl since both are added to the libc all together. + dnl Hence, we'd like to go with DATADIRNAME=share and + dnl and CATOBJEXT=.gmo in this case. + AC_CHECK_FUNC(bind_textdomain_codeset, + [CATOBJEXT=.gmo + DATADIRNAME=share], + [CATOBJEXT=.mo + DATADIRNAME=lib]) + ;; + *) + CATOBJEXT=.mo + DATADIRNAME=lib + ;; + esac]) + LIBS="$glib_save_LIBS" + INSTOBJEXT=.mo + else + gt_cv_have_gettext=no + fi + fi + ]) + + if test "$gt_cv_have_gettext" = "yes" ; then + AC_DEFINE(ENABLE_NLS, 1, + [always defined to indicate that i18n is enabled]) + fi + + dnl Test whether we really found GNU xgettext. + if test "$XGETTEXT" != ":"; then + dnl If it is not GNU xgettext we define it as : so that the + dnl Makefiles still can work. + if $XGETTEXT --omit-header /dev/null 2> /dev/null; then + : ; + else + AC_MSG_RESULT( + [found xgettext program is not GNU xgettext; ignore it]) + XGETTEXT=":" + fi + fi + + # We need to process the po/ directory. + POSUB=po + + AC_OUTPUT_COMMANDS( + [case "$CONFIG_FILES" in *po/Makefile.in*) + sed -e "/POTFILES =/r po/POTFILES" po/Makefile.in > po/Makefile + esac]) + + dnl These rules are solely for the distribution goal. While doing this + dnl we only have to keep exactly one list of the available catalogs + dnl in configure.in. + for lang in $ALL_LINGUAS; do + GMOFILES="$GMOFILES $lang.gmo" + POFILES="$POFILES $lang.po" + done + + dnl Make all variables we use known to autoconf. + AC_SUBST(CATALOGS) + AC_SUBST(CATOBJEXT) + AC_SUBST(DATADIRNAME) + AC_SUBST(GMOFILES) + AC_SUBST(INSTOBJEXT) + AC_SUBST(INTLLIBS) + AC_SUBST(PO_IN_DATADIR_TRUE) + AC_SUBST(PO_IN_DATADIR_FALSE) + AC_SUBST(POFILES) + AC_SUBST(POSUB) + ]) + +# AM_GLIB_GNU_GETTEXT +# ------------------- +# Do checks necessary for use of gettext. If a suitable implementation +# of gettext is found in either in libintl or in the C library, +# it will set INTLLIBS to the libraries needed for use of gettext +# and AC_DEFINE() HAVE_GETTEXT and ENABLE_NLS. (The shell variable +# gt_cv_have_gettext will be set to "yes".) It will also call AC_SUBST() +# on various variables needed by the Makefile.in.in installed by +# glib-gettextize. +dnl +glib_DEFUN([GLIB_GNU_GETTEXT], + [AC_REQUIRE([AC_PROG_CC])dnl + AC_REQUIRE([AC_HEADER_STDC])dnl + + GLIB_LC_MESSAGES + GLIB_WITH_NLS + + if test "$gt_cv_have_gettext" = "yes"; then + if test "x$ALL_LINGUAS" = "x"; then + LINGUAS= + else + AC_MSG_CHECKING(for catalogs to be installed) + NEW_LINGUAS= + for presentlang in $ALL_LINGUAS; do + useit=no + if test "%UNSET%" != "${LINGUAS-%UNSET%}"; then + desiredlanguages="$LINGUAS" + else + desiredlanguages="$ALL_LINGUAS" + fi + for desiredlang in $desiredlanguages; do + # Use the presentlang catalog if desiredlang is + # a. equal to presentlang, or + # b. a variant of presentlang (because in this case, + # presentlang can be used as a fallback for messages + # which are not translated in the desiredlang catalog). + case "$desiredlang" in + "$presentlang"*) useit=yes;; + esac + done + if test $useit = yes; then + NEW_LINGUAS="$NEW_LINGUAS $presentlang" + fi + done + LINGUAS=$NEW_LINGUAS + AC_MSG_RESULT($LINGUAS) + fi + + dnl Construct list of names of catalog files to be constructed. + if test -n "$LINGUAS"; then + for lang in $LINGUAS; do CATALOGS="$CATALOGS $lang$CATOBJEXT"; done + fi + fi + + dnl If the AC_CONFIG_AUX_DIR macro for autoconf is used we possibly + dnl find the mkinstalldirs script in another subdir but ($top_srcdir). + dnl Try to locate is. + MKINSTALLDIRS= + if test -n "$ac_aux_dir"; then + MKINSTALLDIRS="$ac_aux_dir/mkinstalldirs" + fi + if test -z "$MKINSTALLDIRS"; then + MKINSTALLDIRS="\$(top_srcdir)/mkinstalldirs" + fi + AC_SUBST(MKINSTALLDIRS) + + dnl Generate list of files to be processed by xgettext which will + dnl be included in po/Makefile. + test -d po || mkdir po + if test "x$srcdir" != "x."; then + if test "x`echo $srcdir | sed 's@/.*@@'`" = "x"; then + posrcprefix="$srcdir/" + else + posrcprefix="../$srcdir/" + fi + else + posrcprefix="../" + fi + rm -f po/POTFILES + sed -e "/^#/d" -e "/^\$/d" -e "s,.*, $posrcprefix& \\\\," -e "\$s/\(.*\) \\\\/\1/" \ + < $srcdir/po/POTFILES.in > po/POTFILES + ]) + +# AM_GLIB_DEFINE_LOCALEDIR(VARIABLE) +# ------------------------------- +# Define VARIABLE to the location where catalog files will +# be installed by po/Makefile. +glib_DEFUN([GLIB_DEFINE_LOCALEDIR], +[glib_REQUIRE([GLIB_GNU_GETTEXT])dnl +glib_save_prefix="$prefix" +glib_save_exec_prefix="$exec_prefix" +glib_save_datarootdir="$datarootdir" +test "x$prefix" = xNONE && prefix=$ac_default_prefix +test "x$exec_prefix" = xNONE && exec_prefix=$prefix +datarootdir=`eval echo "${datarootdir}"` +if test "x$CATOBJEXT" = "x.mo" ; then + localedir=`eval echo "${libdir}/locale"` +else + localedir=`eval echo "${datadir}/locale"` +fi +prefix="$glib_save_prefix" +exec_prefix="$glib_save_exec_prefix" +datarootdir="$glib_save_datarootdir" +AC_DEFINE_UNQUOTED($1, "$localedir", + [Define the location where the catalogs will be installed]) +]) + +dnl +dnl Now the definitions that aclocal will find +dnl +ifdef(glib_configure_in,[],[ +AC_DEFUN([AM_GLIB_GNU_GETTEXT],[GLIB_GNU_GETTEXT($@)]) +AC_DEFUN([AM_GLIB_DEFINE_LOCALEDIR],[GLIB_DEFINE_LOCALEDIR($@)]) +])dnl + +# GLIB_RUN_PROG(PROGRAM, TEST-FILE, [ACTION-IF-PASS], [ACTION-IF-FAIL]) +# +# Create a temporary file with TEST-FILE as its contents and pass the +# file name to PROGRAM. Perform ACTION-IF-PASS if PROGRAM exits with +# 0 and perform ACTION-IF-FAIL for any other exit status. +AC_DEFUN([GLIB_RUN_PROG], +[cat >conftest.foo <<_ACEOF +$2 +_ACEOF +if AC_RUN_LOG([$1 conftest.foo]); then + m4_ifval([$3], [$3], [:]) +m4_ifvaln([$4], [else $4])dnl +echo "$as_me: failed input was:" >&AS_MESSAGE_LOG_FD +sed 's/^/| /' conftest.foo >&AS_MESSAGE_LOG_FD +fi]) + diff --git a/validate/common/m4/gst-arch.m4 b/validate/common/m4/gst-arch.m4 new file mode 100644 index 0000000..4ad7680 --- /dev/null +++ b/validate/common/m4/gst-arch.m4 @@ -0,0 +1,147 @@ +dnl AG_GST_ARCH +dnl sets up defines and automake conditionals for host architecture +dnl checks endianness +dnl defines HOST_CPU + +AC_DEFUN([AG_GST_ARCH], +[ + dnl Determine CPU + case "x${target_cpu}" in + xi?86 | xk? | xi?86_64) + case $target_os in + solaris*) + AC_CHECK_DECL([__i386], [I386_ABI="yes"], [I386_ABI="no"]) + AC_CHECK_DECL([__amd64], [AMD64_ABI="yes"], [AMD64_ABI="no"]) + + if test "x$I386_ABI" = "xyes" ; then + HAVE_CPU_I386=yes + AC_DEFINE(HAVE_CPU_I386, 1, [Define if the target CPU is an x86]) + fi + if test "x$AMD64_ABI" = "xyes" ; then + HAVE_CPU_X86_64=yes + AC_DEFINE(HAVE_CPU_X86_64, 1, [Define if the target CPU is a x86_64]) + fi + ;; + *) + HAVE_CPU_I386=yes + AC_DEFINE(HAVE_CPU_I386, 1, [Define if the target CPU is an x86]) + + dnl FIXME could use some better detection + dnl (ie CPUID) + case "x${target_cpu}" in + xi386 | xi486) ;; + *) + AC_DEFINE(HAVE_RDTSC, 1, [Define if RDTSC is available]) ;; + esac + ;; + esac + ;; + xpowerpc) + HAVE_CPU_PPC=yes + AC_DEFINE(HAVE_CPU_PPC, 1, [Define if the target CPU is a PowerPC]) ;; + xpowerpc64) + HAVE_CPU_PPC64=yes + AC_DEFINE(HAVE_CPU_PPC64, 1, [Define if the target CPU is a 64 bit PowerPC]) ;; + xalpha*) + HAVE_CPU_ALPHA=yes + AC_DEFINE(HAVE_CPU_ALPHA, 1, [Define if the target CPU is an Alpha]) ;; + xarc*) + HAVE_CPU_ARC=yes + AC_DEFINE(HAVE_CPU_ARC, 1, [Define if the target CPU is an ARC]) ;; + xarm*) + HAVE_CPU_ARM=yes + AC_DEFINE(HAVE_CPU_ARM, 1, [Define if the target CPU is an ARM]) ;; + xaarch64*) + HAVE_CPU_AARCH64=yes + AC_DEFINE(HAVE_CPU_AARCH64, 1, [Define if the target CPU is AARCH64]) ;; + xsparc*) + HAVE_CPU_SPARC=yes + AC_DEFINE(HAVE_CPU_SPARC, 1, [Define if the target CPU is a SPARC]) ;; + xmips*) + HAVE_CPU_MIPS=yes + AC_DEFINE(HAVE_CPU_MIPS, 1, [Define if the target CPU is a MIPS]) ;; + xhppa*) + HAVE_CPU_HPPA=yes + AC_DEFINE(HAVE_CPU_HPPA, 1, [Define if the target CPU is a HPPA]) ;; + xs390*) + HAVE_CPU_S390=yes + AC_DEFINE(HAVE_CPU_S390, 1, [Define if the target CPU is a S390]) ;; + xia64*) + HAVE_CPU_IA64=yes + AC_DEFINE(HAVE_CPU_IA64, 1, [Define if the target CPU is a IA64]) ;; + xm68k*) + HAVE_CPU_M68K=yes + AC_DEFINE(HAVE_CPU_M68K, 1, [Define if the target CPU is a M68K]) ;; + xx86_64) + HAVE_CPU_X86_64=yes + AC_DEFINE(HAVE_CPU_X86_64, 1, [Define if the target CPU is a x86_64]) ;; + xcris) + HAVE_CPU_CRIS=yes + AC_DEFINE(HAVE_CPU_CRIS, 1, [Define if the target CPU is a CRIS]) ;; + xcrisv32) + HAVE_CPU_CRISV32=yes + AC_DEFINE(HAVE_CPU_CRISV32, 1, [Define if the target CPU is a CRISv32]) ;; + esac + + dnl Determine endianness + AC_C_BIGENDIAN + + AM_CONDITIONAL(HAVE_CPU_I386, test "x$HAVE_CPU_I386" = "xyes") + AM_CONDITIONAL(HAVE_CPU_PPC, test "x$HAVE_CPU_PPC" = "xyes") + AM_CONDITIONAL(HAVE_CPU_PPC64, test "x$HAVE_CPU_PPC64" = "xyes") + AM_CONDITIONAL(HAVE_CPU_ALPHA, test "x$HAVE_CPU_ALPHA" = "xyes") + AM_CONDITIONAL(HAVE_CPU_ARC, test "x$HAVE_CPU_ARC" = "xyes") + AM_CONDITIONAL(HAVE_CPU_ARM, test "x$HAVE_CPU_ARM" = "xyes") + AM_CONDITIONAL(HAVE_CPU_SPARC, test "x$HAVE_CPU_SPARC" = "xyes") + AM_CONDITIONAL(HAVE_CPU_HPPA, test "x$HAVE_CPU_HPPA" = "xyes") + AM_CONDITIONAL(HAVE_CPU_MIPS, test "x$HAVE_CPU_MIPS" = "xyes") + AM_CONDITIONAL(HAVE_CPU_S390, test "x$HAVE_CPU_S390" = "xyes") + AM_CONDITIONAL(HAVE_CPU_IA64, test "x$HAVE_CPU_IA64" = "xyes") + AM_CONDITIONAL(HAVE_CPU_M68K, test "x$HAVE_CPU_M68K" = "xyes") + AM_CONDITIONAL(HAVE_CPU_X86_64, test "x$HAVE_CPU_X86_64" = "xyes") + AM_CONDITIONAL(HAVE_CPU_CRIS, test "x$HAVE_CPU_CRIS" = "xyes") + AM_CONDITIONAL(HAVE_CPU_CRISV32, test "x$HAVE_CPU_CRISV32" = "xyes") + + AC_DEFINE_UNQUOTED(HOST_CPU, "$host_cpu", [the host CPU]) + AC_DEFINE_UNQUOTED(TARGET_CPU, "$target_cpu", [the target CPU]) +]) + +dnl check if unaligned memory access works correctly +AC_DEFUN([AG_GST_UNALIGNED_ACCESS], [ + AC_MSG_CHECKING([if unaligned memory access works correctly]) + if test x"$as_cv_unaligned_access" = x ; then + case $host in + alpha*|arc*|arm*|aarch64*|hp*|mips*|sh*|sparc*|ia64*) + _AS_ECHO_N([(blacklisted) ]) + as_cv_unaligned_access=no + ;; + i?86*|x86_64*|amd64*|powerpc*|m68k*|cris*) + _AS_ECHO_N([(whitelisted) ]) + as_cv_unaligned_access=yes + ;; + esac + else + _AS_ECHO_N([(cached) ]) + fi + if test x"$as_cv_unaligned_access" = x ; then + AC_TRY_RUN([ +int main(int argc, char **argv) +{ + char array[] = "ABCDEFGH"; + unsigned int iarray[2]; + memcpy(iarray,array,8); +#define GET(x) (*(unsigned int *)((char *)iarray + (x))) + if(GET(0) != 0x41424344 && GET(0) != 0x44434241) return 1; + if(GET(1) != 0x42434445 && GET(1) != 0x45444342) return 1; + if(GET(2) != 0x43444546 && GET(2) != 0x46454443) return 1; + if(GET(3) != 0x44454647 && GET(3) != 0x47464544) return 1; + return 0; +} + ], as_cv_unaligned_access="yes", as_cv_unaligned_access="no") + fi + AC_MSG_RESULT($as_cv_unaligned_access) + if test "$as_cv_unaligned_access" = "yes"; then + AC_DEFINE_UNQUOTED(HAVE_UNALIGNED_ACCESS, 1, + [defined if unaligned memory access works correctly]) + fi +]) diff --git a/validate/common/m4/gst-args.m4 b/validate/common/m4/gst-args.m4 new file mode 100644 index 0000000..b478c82 --- /dev/null +++ b/validate/common/m4/gst-args.m4 @@ -0,0 +1,360 @@ +dnl configure-time options shared among gstreamer modules + +dnl AG_GST_ARG_DEBUG +dnl AG_GST_ARG_PROFILING +dnl AG_GST_ARG_VALGRIND +dnl AG_GST_ARG_GCOV + +dnl AG_GST_ARG_EXAMPLES + +dnl AG_GST_ARG_WITH_PKG_CONFIG_PATH +dnl AG_GST_ARG_WITH_PACKAGE_NAME +dnl AG_GST_ARG_WITH_PACKAGE_ORIGIN + +dnl AG_GST_ARG_WITH_PLUGINS +dnl AG_GST_CHECK_PLUGIN +dnl AG_GST_DISABLE_PLUGIN + +dnl AG_GST_ARG_ENABLE_EXTERNAL +dnl AG_GST_ARG_ENABLE_EXPERIMENTAL +dnl AG_GST_ARG_ENABLE_BROKEN + +dnl AG_GST_ARG_DISABLE_FATAL_WARNINGS +AC_DEFUN([AG_GST_ARG_DEBUG], +[ + dnl debugging stuff + AC_ARG_ENABLE(debug, + AC_HELP_STRING([--disable-debug],[disable addition of -g debugging info]), + [ + case "${enableval}" in + yes) USE_DEBUG=yes ;; + no) USE_DEBUG=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-debug) ;; + esac + ], + [USE_DEBUG=yes]) dnl Default value +]) + +AC_DEFUN([AG_GST_ARG_PROFILING], +[ + AC_ARG_ENABLE(profiling, + AC_HELP_STRING([--enable-profiling], + [adds -pg to compiler commandline, for profiling]), + [ + case "${enableval}" in + yes) USE_PROFILING=yes ;; + no) USE_PROFILING=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-profiling) ;; + esac + ], + [USE_PROFILING=no]) dnl Default value +]) + +AC_DEFUN([AG_GST_ARG_VALGRIND], +[ + dnl valgrind inclusion + AC_ARG_ENABLE(valgrind, + AC_HELP_STRING([--disable-valgrind],[disable run-time valgrind detection]), + [ + case "${enableval}" in + yes) USE_VALGRIND="$USE_DEBUG" ;; + no) USE_VALGRIND=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-valgrind) ;; + esac + ], + [USE_VALGRIND="$USE_DEBUG"]) dnl Default value + VALGRIND_REQ="3.0" + if test "x$USE_VALGRIND" = xyes; then + PKG_CHECK_MODULES(VALGRIND, valgrind >= $VALGRIND_REQ, + USE_VALGRIND="yes", + USE_VALGRIND="no") + fi + if test "x$USE_VALGRIND" = xyes; then + AC_DEFINE(HAVE_VALGRIND, 1, [Define if valgrind should be used]) + AC_MSG_NOTICE(Using extra code paths for valgrind) + fi +]) + +AC_DEFUN([AG_GST_ARG_GCOV], +[ + AC_ARG_ENABLE(gcov, + AC_HELP_STRING([--enable-gcov], + [compile with coverage profiling instrumentation (gcc only)]), + enable_gcov=$enableval, + enable_gcov=no) + if test x$enable_gcov = xyes ; then + if test "x$GCC" != "xyes" + then + AC_MSG_ERROR([gcov only works if gcc is used]) + fi + + AS_COMPILER_FLAG(["-fprofile-arcs"], + [GCOV_CFLAGS="$GCOV_CFLAGS -fprofile-arcs"], + true) + AS_COMPILER_FLAG(["-ftest-coverage"], + [GCOV_CFLAGS="$GCOV_CFLAGS -ftest-coverage"], + true) + dnl remove any -O flags - FIXME: is this needed ? + GCOV_CFLAGS=`echo "$GCOV_CFLAGS" | sed -e 's/-O[[0-9]]*//g'` + dnl libtool 1.5.22 and lower strip -fprofile-arcs from the flags + dnl passed to the linker, which is a bug; -fprofile-arcs implicitly + dnl links in -lgcov, so we do it explicitly here for the same effect + GCOV_LIBS=-lgcov + AC_SUBST(GCOV_CFLAGS) + AC_SUBST(GCOV_LIBS) + GCOV=`echo $CC | sed s/gcc/gcov/g` + AC_SUBST(GCOV) + + GST_GCOV_ENABLED=yes + AC_DEFINE_UNQUOTED(GST_GCOV_ENABLED, 1, + [Defined if gcov is enabled to force a rebuild due to config.h changing]) + dnl if gcov is used, we do not want default -O2 CFLAGS + if test "x$GST_GCOV_ENABLED" = "xyes" + then + CFLAGS="$CFLAGS -O0" + AC_SUBST(CFLAGS) + CXXFLAGS="$CXXFLAGS -O0" + AC_SUBST(CXXFLAGS) + FFLAGS="$FFLAGS -O0" + AC_SUBST(FFLAGS) + CCASFLAGS="$CCASFLAGS -O0" + AC_SUBST(CCASFLAGS) + AC_MSG_NOTICE([gcov enabled, setting CFLAGS and friends to $CFLAGS]) + fi + fi + AM_CONDITIONAL(GST_GCOV_ENABLED, test x$enable_gcov = xyes) +]) + +AC_DEFUN([AG_GST_ARG_EXAMPLES], +[ + AC_ARG_ENABLE(examples, + AC_HELP_STRING([--disable-examples], [disable building examples]), + [ + case "${enableval}" in + yes) BUILD_EXAMPLES=yes ;; + no) BUILD_EXAMPLES=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for --disable-examples) ;; + esac + ], + [BUILD_EXAMPLES=yes]) dnl Default value + AM_CONDITIONAL(BUILD_EXAMPLES, test "x$BUILD_EXAMPLES" = "xyes") +]) + +AC_DEFUN([AG_GST_ARG_WITH_PKG_CONFIG_PATH], +[ + dnl possibly modify pkg-config path + AC_ARG_WITH(pkg-config-path, + AC_HELP_STRING([--with-pkg-config-path], + [colon-separated list of pkg-config(1) dirs]), + [ + export PKG_CONFIG_PATH=${withval} + AC_MSG_NOTICE(Set PKG_CONFIG_PATH to $PKG_CONFIG_PATH) + ]) +]) + + +dnl This macro requires that GST_GIT or GST_CVS is set to yes or no (release) +AC_DEFUN([AG_GST_ARG_WITH_PACKAGE_NAME], +[ + dnl package name in plugins + AC_ARG_WITH(package-name, + AC_HELP_STRING([--with-package-name], + [specify package name to use in plugins]), + [ + case "${withval}" in + yes) AC_MSG_ERROR(bad value ${withval} for --with-package-name) ;; + no) AC_MSG_ERROR(bad value ${withval} for --with-package-name) ;; + *) GST_PACKAGE_NAME="${withval}" ;; + esac + ], + [ + P=$1 + if test "x$P" = "x" + then + P=$PACKAGE_NAME + fi + + if test "x$PACKAGE_VERSION_NANO" = "x0" + then + GST_PACKAGE_NAME="$P source release" + else + if test "x$PACKAGE_VERSION_NANO" = "x1" + then + GST_PACKAGE_NAME="$P git" + else + GST_PACKAGE_NAME="$P prerelease" + fi + fi + ] + ) + AC_MSG_NOTICE(Using $GST_PACKAGE_NAME as package name) + AC_DEFINE_UNQUOTED(GST_PACKAGE_NAME, "$GST_PACKAGE_NAME", + [package name in plugins]) + AC_SUBST(GST_PACKAGE_NAME) +]) + +AC_DEFUN([AG_GST_ARG_WITH_PACKAGE_ORIGIN], +[ + dnl package origin URL + AC_ARG_WITH(package-origin, + AC_HELP_STRING([--with-package-origin], + [specify package origin URL to use in plugins]), + [ + case "${withval}" in + yes) AC_MSG_ERROR(bad value ${withval} for --with-package-origin) ;; + no) AC_MSG_ERROR(bad value ${withval} for --with-package-origin) ;; + *) GST_PACKAGE_ORIGIN="${withval}" ;; + esac + ], + [GST_PACKAGE_ORIGIN="[Unknown package origin]"] dnl Default value + ) + AC_MSG_NOTICE(Using $GST_PACKAGE_ORIGIN as package origin) + AC_DEFINE_UNQUOTED(GST_PACKAGE_ORIGIN, "$GST_PACKAGE_ORIGIN", + [package origin]) + AC_SUBST(GST_PACKAGE_ORIGIN) +]) + +dnl sets WITH_PLUGINS to the list of plug-ins given as an argument +dnl also clears GST_PLUGINS_ALL and GST_PLUGINS_SELECTED +AC_DEFUN([AG_GST_ARG_WITH_PLUGINS], +[ + AC_ARG_WITH(plugins, + AC_HELP_STRING([--with-plugins], + [comma-separated list of dependencyless plug-ins to compile]), + [WITH_PLUGINS=$withval], + [WITH_PLUGINS=]) + + GST_PLUGINS_ALL="" + GST_PLUGINS_SELECTED="" + GST_PLUGINS_NONPORTED="" + + AC_SUBST(GST_PLUGINS_ALL) + AC_SUBST(GST_PLUGINS_SELECTED) + AC_SUBST(GST_PLUGINS_NONPORTED) +]) + +dnl AG_GST_CHECK_PLUGIN(PLUGIN-NAME) +dnl +dnl This macro adds the plug-in to GST_PLUGINS_ALL. Then it +dnl checks if WITH_PLUGINS is empty or the plugin is present in WITH_PLUGINS, +dnl and if so adds it to GST_PLUGINS_SELECTED. Then it checks if the plugin +dnl is present in WITHOUT_PLUGINS (ie. was disabled specifically) and if so +dnl removes it from GST_PLUGINS_SELECTED. +dnl +dnl The macro will call AM_CONDITIONAL(USE_PLUGIN_, ...) to allow +dnl control of what is built in Makefile.ams. +AC_DEFUN([AG_GST_CHECK_PLUGIN], +[ + GST_PLUGINS_ALL="$GST_PLUGINS_ALL [$1]" + + define([pname_def],translit([$1], -a-z, _a-z)) + + AC_ARG_ENABLE([$1], + AC_HELP_STRING([--disable-[$1]], [disable dependency-less $1 plugin]), + [ + case "${enableval}" in + yes) [gst_use_]pname_def=yes ;; + no) [gst_use_]pname_def=no ;; + *) AC_MSG_ERROR([bad value ${enableval} for --enable-$1]) ;; + esac + ], + [[gst_use_]pname_def=yes]) dnl Default value + + if test x$[gst_use_]pname_def = xno; then + AC_MSG_NOTICE(disabling dependency-less plugin $1) + WITHOUT_PLUGINS="$WITHOUT_PLUGINS [$1]" + fi + undefine([pname_def]) + + dnl First check inclusion + if [[ -z "$WITH_PLUGINS" ]] || echo " [$WITH_PLUGINS] " | tr , ' ' | grep -i " [$1] " > /dev/null; then + GST_PLUGINS_SELECTED="$GST_PLUGINS_SELECTED [$1]" + fi + dnl Then check exclusion + if echo " [$WITHOUT_PLUGINS] " | tr , ' ' | grep -i " [$1] " > /dev/null; then + GST_PLUGINS_SELECTED=`echo " $GST_PLUGINS_SELECTED " | $SED -e 's/ [$1] / /'` + fi + dnl Finally check if the plugin is ported or not + if echo " [$GST_PLUGINS_NONPORTED] " | tr , ' ' | grep -i " [$1] " > /dev/null; then + GST_PLUGINS_SELECTED=`echo " $GST_PLUGINS_SELECTED " | $SED -e 's/ [$1] / /'` + fi + AM_CONDITIONAL([USE_PLUGIN_]translit([$1], a-z, A-Z), echo " $GST_PLUGINS_SELECTED " | grep -i " [$1] " > /dev/null) +]) + +dnl AG_GST_DISABLE_PLUGIN(PLUGIN-NAME) +dnl +dnl This macro disables the plug-in by removing it from +dnl GST_PLUGINS_SELECTED. +AC_DEFUN([AG_GST_DISABLE_PLUGIN], +[ + GST_PLUGINS_SELECTED=`echo " $GST_PLUGINS_SELECTED " | $SED -e 's/ [$1] / /'` + AM_CONDITIONAL([USE_PLUGIN_]translit([$1], a-z, A-Z), false) +]) + +AC_DEFUN([AG_GST_ARG_ENABLE_EXTERNAL], +[ + AG_GST_CHECK_FEATURE(EXTERNAL, [building of plug-ins with external deps],, + HAVE_EXTERNAL=yes, enabled, + [ + AC_MSG_NOTICE(building external plug-ins) + BUILD_EXTERNAL="yes" + ],[ + AC_MSG_WARN(all plug-ins with external dependencies will not be built) + BUILD_EXTERNAL="no" + ]) + # make BUILD_EXTERNAL available to Makefile.am + AM_CONDITIONAL(BUILD_EXTERNAL, test "x$BUILD_EXTERNAL" = "xyes") +]) + +dnl experimental plug-ins; stuff that hasn't had the dust settle yet +dnl read 'builds, but might not work' +AC_DEFUN([AG_GST_ARG_ENABLE_EXPERIMENTAL], +[ + AG_GST_CHECK_FEATURE(EXPERIMENTAL, [building of experimental plug-ins],, + HAVE_EXPERIMENTAL=yes, disabled, + [ + AC_MSG_WARN(building experimental plug-ins) + BUILD_EXPERIMENTAL="yes" + ],[ + AC_MSG_NOTICE(not building experimental plug-ins) + BUILD_EXPERIMENTAL="no" + ]) + # make BUILD_EXPERIMENTAL available to Makefile.am + AM_CONDITIONAL(BUILD_EXPERIMENTAL, test "x$BUILD_EXPERIMENTAL" = "xyes") +]) + +dnl broken plug-ins; stuff that doesn't seem to build at the moment +AC_DEFUN([AG_GST_ARG_ENABLE_BROKEN], +[ + AG_GST_CHECK_FEATURE(BROKEN, [building of broken plug-ins],, + HAVE_BROKEN=yes, disabled, + [ + AC_MSG_WARN([building broken plug-ins -- no bug reports on these, only patches ...]) + ],[ + AC_MSG_NOTICE([not building broken plug-ins]) + ]) +]) + +dnl allow people (or build tools) to override default behaviour +dnl for fatal compiler warnings +dnl Enable fatal warnings by default only for development versions +AC_DEFUN([AG_GST_ARG_DISABLE_FATAL_WARNINGS], +[ + AC_ARG_ENABLE(fatal-warnings, + AC_HELP_STRING([--disable-fatal-warnings], + [Don't turn compiler warnings into fatal errors]), + [ + case "${enableval}" in + yes) FATAL_WARNINGS=yes ;; + no) FATAL_WARNINGS=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for --disable-fatal-warnings) ;; + esac + ], + [ + if test "x`expr $PACKAGE_VERSION_MINOR % 2`" = "x1" -a "x`expr $PACKAGE_VERSION_MICRO '<' 90`" = "x1"; then + FATAL_WARNINGS=yes + else + FATAL_WARNINGS=no + fi + ]) +]) diff --git a/validate/common/m4/gst-check.m4 b/validate/common/m4/gst-check.m4 new file mode 100644 index 0000000..4277053 --- /dev/null +++ b/validate/common/m4/gst-check.m4 @@ -0,0 +1,294 @@ +dnl pkg-config-based checks for GStreamer modules and dependency modules + +dnl generic: +dnl AG_GST_PKG_CHECK_MODULES([PREFIX], [WHICH], [REQUIRED]) +dnl sets HAVE_[$PREFIX], [$PREFIX]_* +dnl AG_GST_CHECK_MODULES([PREFIX], [MODULE], [MINVER], [NAME], [REQUIRED]) +dnl sets HAVE_[$PREFIX], [$PREFIX]_* + +dnl specific: +dnl AG_GST_CHECK_GST([MAJMIN], [MINVER], [REQUIRED]) +dnl also sets/ACSUBSTs GST_TOOLS_DIR and GST_PLUGINS_DIR +dnl AG_GST_CHECK_GST_BASE([MAJMIN], [MINVER], [REQUIRED]) +dnl AG_GST_CHECK_GST_CONTROLLER([MAJMIN], [MINVER], [REQUIRED]) +dnl AG_GST_CHECK_GST_NET([MAJMIN], [MINVER], [REQUIRED]) +dnl AG_GST_CHECK_GST_CHECK([MAJMIN], [MINVER], [REQUIRED]) +dnl AG_GST_CHECK_GST_PLUGINS_BASE([MAJMIN], [MINVER], [REQUIRED]) +dnl also sets/ACSUBSTs GSTPB_PLUGINS_DIR + +AC_DEFUN([AG_GST_PKG_CHECK_MODULES], +[ + which="[$2]" + dnl not required by default, since we use this mostly for plugin deps + required=ifelse([$3], , "no", [$3]) + + PKG_CHECK_MODULES([$1], $which, + [ + HAVE_[$1]="yes" + ], + [ + HAVE_[$1]="no" + if test "x$required" = "xyes"; then + AC_MSG_ERROR($[$1]_PKG_ERRORS) + else + AC_MSG_NOTICE($[$1]_PKG_ERRORS) + fi + ]) + + dnl AC_SUBST of CFLAGS and LIBS was not done before automake 1.7 + dnl It gets done automatically in automake >= 1.7, which we now require +])) + +AC_DEFUN([AG_GST_CHECK_MODULES], +[ + module=[$2] + minver=[$3] + name="[$4]" + required=ifelse([$5], , "yes", [$5]) dnl required by default + + PKG_CHECK_MODULES([$1], $module >= $minver, + [ + HAVE_[$1]="yes" + ], + [ + HAVE_[$1]="no" + AC_MSG_NOTICE($[$1]_PKG_ERRORS) + if test "x$required" = "xyes"; then + AC_MSG_ERROR([no $module >= $minver ($name) found]) + else + AC_MSG_NOTICE([no $module >= $minver ($name) found]) + fi + ]) + + dnl AC_SUBST of CFLAGS and LIBS was not done before automake 1.7 + dnl It gets done automatically in automake >= 1.7, which we now require +])) + +AC_DEFUN([AG_GST_CHECK_GST], +[ + AG_GST_CHECK_MODULES(GST, gstreamer-[$1], [$2], [GStreamer], [$3]) + dnl allow setting before calling this macro to override + if test -z $GST_TOOLS_DIR; then + GST_TOOLS_DIR=`$PKG_CONFIG --variable=toolsdir gstreamer-[$1]` + if test -z $GST_TOOLS_DIR; then + AC_MSG_ERROR( + [no tools dir set in GStreamer pkg-config file, core upgrade needed.]) + fi + fi + AC_MSG_NOTICE([using GStreamer tools in $GST_TOOLS_DIR]) + AC_SUBST(GST_TOOLS_DIR) + + dnl check for where core plug-ins got installed + dnl this is used for unit tests + dnl allow setting before calling this macro to override + if test -z $GST_PLUGINS_DIR; then + GST_PLUGINS_DIR=`$PKG_CONFIG --variable=pluginsdir gstreamer-[$1]` + if test -z $GST_PLUGINS_DIR; then + AC_MSG_ERROR( + [no pluginsdir set in GStreamer pkg-config file, core upgrade needed.]) + fi + fi + AC_MSG_NOTICE([using GStreamer plug-ins in $GST_PLUGINS_DIR]) + AC_SUBST(GST_PLUGINS_DIR) +]) + +AC_DEFUN([AG_GST_CHECK_GST_BASE], +[ + AG_GST_CHECK_MODULES(GST_BASE, gstreamer-base-[$1], [$2], + [GStreamer Base Libraries], [$3]) +]) + +AC_DEFUN([AG_GST_CHECK_GST_CONTROLLER], +[ + AG_GST_CHECK_MODULES(GST_CONTROLLER, gstreamer-controller-[$1], [$2], + [GStreamer Controller Library], [$3]) +]) + +AC_DEFUN([AG_GST_CHECK_GST_NET], +[ + AG_GST_CHECK_MODULES(GST_NET, gstreamer-net-[$1], [$2], + [GStreamer Network Library], [$3]) +]) + +AC_DEFUN([AG_GST_CHECK_GST_CHECK], +[ + AG_GST_CHECK_MODULES(GST_CHECK, gstreamer-check-[$1], [$2], + [GStreamer Check unittest Library], [$3]) +]) + +dnl =========================================================================== +dnl AG_GST_CHECK_UNINSTALLED_SETUP([ACTION-IF-UNINSTALLED], [ACTION-IF-NOT]) +dnl +dnl ACTION-IF-UNINSTALLED (optional) extra actions to perform if the setup +dnl is an uninstalled setup +dnl ACTION-IF-NOT (optional) extra actions to perform if the setup +dnl is not an uninstalled setup +dnl =========================================================================== +AC_DEFUN([AG_GST_CHECK_UNINSTALLED_SETUP], +[ + AC_MSG_CHECKING([whether this is an uninstalled GStreamer setup]) + AC_CACHE_VAL(gst_cv_is_uninstalled_setup,[ + gst_cv_is_uninstalled_setup=no + if (set -u; : $GST_PLUGIN_SYSTEM_PATH) 2>/dev/null ; then + if test -z "$GST_PLUGIN_SYSTEM_PATH" \ + -a -n "$GST_PLUGIN_SCANNER" \ + -a -n "$GST_PLUGIN_PATH" \ + -a -n "$GST_REGISTRY" \ + -a -n "$DYLD_LIBRARY_PATH" \ + -a -n "$LD_LIBRARY_PATH"; then + gst_cv_is_uninstalled_setup=yes; + fi + fi + ]) + AC_MSG_RESULT($gst_cv_is_uninstalled_setup) + if test "x$gst_cv_is_uninstalled_setup" = "xyes"; then + ifelse([$1], , :, [$1]) + else + ifelse([$2], , :, [$2]) + fi +]) + +dnl =========================================================================== +dnl AG_GST_CHECK_GST_PLUGINS_BASE([GST-API_VERSION], [MIN-VERSION], [REQUIRED]) +dnl +dnl Sets GST_PLUGINS_BASE_CFLAGS and GST_PLUGINS_BASE_LIBS. +dnl +dnl Also sets GSTPB_PLUGINS_DIR (and for consistency also GST_PLUGINS_BASE_DIR) +dnl for use in Makefile.am. This is only really needed/useful in uninstalled +dnl setups, since in an installed setup all plugins will be found in +dnl GST_PLUGINS_DIR anyway. +dnl =========================================================================== +AC_DEFUN([AG_GST_CHECK_GST_PLUGINS_BASE], +[ + AG_GST_CHECK_MODULES(GST_PLUGINS_BASE, gstreamer-plugins-base-[$1], [$2], + [GStreamer Base Plugins], [$3]) + + if test "x$HAVE_GST_PLUGINS_BASE" = "xyes"; then + dnl check for where base plugins got installed + dnl this is used for unit tests + dnl allow setting before calling this macro to override + if test -z $GSTPB_PLUGINS_DIR; then + GSTPB_PLUGINS_DIR=`$PKG_CONFIG --variable=pluginsdir gstreamer-plugins-base-[$1]` + if test -z $GSTPB_PLUGINS_DIR; then + AC_MSG_ERROR( + [no pluginsdir set in GStreamer Base Plugins pkg-config file]) + fi + fi + AC_MSG_NOTICE([using GStreamer Base Plugins in $GSTPB_PLUGINS_DIR]) + GST_PLUGINS_BASE_DIR="$GSTPB_PLUGINS_DIR/gst:$GSTPB_PLUGINS_DIR/sys:$GSTPB_PLUGINS_DIR/ext" + AC_SUBST(GST_PLUGINS_BASE_DIR) + AC_SUBST(GSTPB_PLUGINS_DIR) + fi +]) + +dnl =========================================================================== +dnl AG_GST_CHECK_GST_PLUGINS_GOOD([GST-API_VERSION], [MIN-VERSION]) +dnl +dnl Will set GST_PLUGINS_GOOD_DIR for use in Makefile.am. Note that this will +dnl only be set in an uninstalled setup, since -good ships no .pc file and in +dnl an installed setup all plugins will be found in GST_PLUGINS_DIR anyway. +dnl =========================================================================== +AC_DEFUN([AG_GST_CHECK_GST_PLUGINS_GOOD], +[ + AG_GST_CHECK_MODULES(GST_PLUGINS_GOOD, gstreamer-plugins-good-[$1], [$2], + [GStreamer Good Plugins], [no]) + + if test "x$HAVE_GST_PLUGINS_GOOD" = "xyes"; then + dnl check for where good plugins got installed + dnl this is used for unit tests + dnl allow setting before calling this macro to override + if test -z $GST_PLUGINS_GOOD_DIR; then + GST_PLUGINS_GOOD_DIR=`$PKG_CONFIG --variable=pluginsdir gstreamer-plugins-good-[$1]` + if test -z $GST_PLUGINS_GOOD_DIR; then + AC_MSG_ERROR([no pluginsdir set in GStreamer Good Plugins pkg-config file]) + fi + fi + AC_MSG_NOTICE([using GStreamer Good Plugins in $GST_PLUGINS_GOOD_DIR]) + GST_PLUGINS_GOOD_DIR="$GST_PLUGINS_GOOD_DIR/gst:$GST_PLUGINS_GOOD_DIR/sys:$GST_PLUGINS_GOOD_DIR/ext" + AC_SUBST(GST_PLUGINS_GOOD_DIR) + fi +]) + +dnl =========================================================================== +dnl AG_GST_CHECK_GST_PLUGINS_UGLY([GST-API_VERSION], [MIN-VERSION]) +dnl +dnl Will set GST_PLUGINS_UGLY_DIR for use in Makefile.am. Note that this will +dnl only be set in an uninstalled setup, since -bad ships no .pc file and in +dnl an installed setup all plugins will be found in GST_PLUGINS_DIR anyway. +dnl =========================================================================== +AC_DEFUN([AG_GST_CHECK_GST_PLUGINS_UGLY], +[ + AG_GST_CHECK_MODULES(GST_PLUGINS_UGLY, gstreamer-plugins-ugly-[$1], [$2], + [GStreamer Ugly Plugins], [no]) + + if test "x$HAVE_GST_PLUGINS_UGLY" = "xyes"; then + dnl check for where ugly plugins got installed + dnl this is used for unit tests + dnl allow setting before calling this macro to override + if test -z $GST_PLUGINS_UGLY_DIR; then + GST_PLUGINS_UGLY_DIR=`$PKG_CONFIG --variable=pluginsdir gstreamer-plugins-ugly-[$1]` + if test -z $GST_PLUGINS_UGLY_DIR; then + AC_MSG_ERROR([no pluginsdir set in GStreamer Ugly Plugins pkg-config file]) + fi + fi + AC_MSG_NOTICE([using GStreamer Ugly Plugins in $GST_PLUGINS_UGLY_DIR]) + GST_PLUGINS_UGLY_DIR="$GST_PLUGINS_UGLY_DIR/gst:$GST_PLUGINS_UGLY_DIR/sys:$GST_PLUGINS_UGLY_DIR/ext" + AC_SUBST(GST_PLUGINS_UGLY_DIR) + fi +]) + +dnl =========================================================================== +dnl AG_GST_CHECK_GST_PLUGINS_BAD([GST-API_VERSION], [MIN-VERSION]) +dnl +dnl Will set GST_PLUGINS_BAD_DIR for use in Makefile.am. Note that this will +dnl only be set in an uninstalled setup, since -ugly ships no .pc file and in +dnl an installed setup all plugins will be found in GST_PLUGINS_DIR anyway. +dnl =========================================================================== +AC_DEFUN([AG_GST_CHECK_GST_PLUGINS_BAD], +[ + AG_GST_CHECK_MODULES(GST_PLUGINS_BAD, gstreamer-plugins-bad-[$1], [$2], + [GStreamer Bad Plugins], [no]) + + if test "x$HAVE_GST_PLUGINS_BAD" = "xyes"; then + dnl check for where bad plugins got installed + dnl this is used for unit tests + dnl allow setting before calling this macro to override + if test -z $GST_PLUGINS_BAD_DIR; then + GST_PLUGINS_BAD_DIR=`$PKG_CONFIG --variable=pluginsdir gstreamer-plugins-bad-[$1]` + if test -z $GST_PLUGINS_BAD_DIR; then + AC_MSG_ERROR([no pluginsdir set in GStreamer Bad Plugins pkg-config file]) + fi + fi + AC_MSG_NOTICE([using GStreamer Bad Plugins in $GST_PLUGINS_BAD_DIR]) + GST_PLUGINS_BAD_DIR="$GST_PLUGINS_BAD_DIR/gst:$GST_PLUGINS_BAD_DIR/sys:$GST_PLUGINS_BAD_DIR/ext" + AC_SUBST(GST_PLUGINS_BAD_DIR) + fi +]) + +dnl =========================================================================== +dnl AG_GST_CHECK_GST_PLUGINS_LIBAV([GST-API_VERSION], [MIN-VERSION]) +dnl +dnl Will set GST_PLUGINS_LIBAV_DIR for use in Makefile.am. Note that this will +dnl only be set in an uninstalled setup, since -libav ships no .pc file and in +dnl an installed setup all plugins will be found in GST_PLUGINS_DIR anyway. +dnl =========================================================================== +AC_DEFUN([AG_GST_CHECK_GST_PLUGINS_LIBAV], +[ + AG_GST_CHECK_MODULES(GST_PLUGINS_LIBAV, gstreamer-plugins-libav-[$1], [$2], + [GStreamer Libav Plugins], [no]) + + if test "x$HAVE_GST_PLUGINS_LIBAV" = "xyes"; then + dnl check for where libav plugins got installed + dnl this is used for unit tests + dnl allow setting before calling this macro to override + if test -z $GST_PLUGINS_LIBAV_DIR; then + GST_PLUGINS_LIBAV_DIR=`$PKG_CONFIG --variable=pluginsdir gstreamer-plugins-libav-[$1]` + if test -z $GST_PLUGINS_LIBAV_DIR; then + AC_MSG_ERROR([no pluginsdir set in GStreamer Libav Plugins pkg-config file]) + fi + fi + GST_PLUGINS_LIBAV_DIR="$GST_PLUGINS_LIBAV_DIR/ext/libav" + AC_MSG_NOTICE([using GStreamer Libav Plugins in $GST_PLUGINS_LIBAV_DIR]) + AC_SUBST(GST_PLUGINS_LIBAV_DIR) + fi +]) diff --git a/validate/common/m4/gst-debuginfo.m4 b/validate/common/m4/gst-debuginfo.m4 new file mode 100644 index 0000000..b48854d --- /dev/null +++ b/validate/common/m4/gst-debuginfo.m4 @@ -0,0 +1,46 @@ +AC_DEFUN([AG_GST_DEBUGINFO], [ +AC_ARG_ENABLE(debug, +AC_HELP_STRING([--disable-debug],[disable addition of -g debugging info]), +[case "${enableval}" in + yes) USE_DEBUG=yes ;; + no) USE_DEBUG=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-debug) ;; +esac], +[USE_DEBUG=yes]) dnl Default value + +AC_ARG_ENABLE(DEBUG, +AC_HELP_STRING([--disable-DEBUG],[disables compilation of debugging messages]), +[case "${enableval}" in + yes) ENABLE_DEBUG=yes ;; + no) ENABLE_DEBUG=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-DEBUG) ;; +esac], +[ENABLE_DEBUG=yes]) dnl Default value +if test x$ENABLE_DEBUG = xyes; then + AC_DEFINE(GST_DEBUG_ENABLED, 1, [Define if DEBUG statements should be compiled in]) +fi + +AC_ARG_ENABLE(INFO, +AC_HELP_STRING([--disable-INFO],[disables compilation of informational messages]), +[case "${enableval}" in + yes) ENABLE_INFO=yes ;; + no) ENABLE_INFO=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-INFO) ;; +esac], +[ENABLE_INFO=yes]) dnl Default value +if test x$ENABLE_INFO = xyes; then + AC_DEFINE(GST_INFO_ENABLED, 1, [Define if INFO statements should be compiled in]) +fi + +AC_ARG_ENABLE(debug-color, +AC_HELP_STRING([--disable-debug-color],[disables color output of DEBUG and INFO output]), +[case "${enableval}" in + yes) ENABLE_DEBUG_COLOR=yes ;; + no) ENABLE_DEBUG_COLOR=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-debug-color) ;; +esac], +[ENABLE_DEBUG_COLOR=yes]) dnl Default value +if test "x$ENABLE_DEBUG_COLOR" = xyes; then + AC_DEFINE(GST_DEBUG_COLOR, 1, [Define if debugging messages should be colorized]) +fi +]) diff --git a/validate/common/m4/gst-default.m4 b/validate/common/m4/gst-default.m4 new file mode 100644 index 0000000..8de9756 --- /dev/null +++ b/validate/common/m4/gst-default.m4 @@ -0,0 +1,120 @@ +dnl default elements used for tests and such + +dnl AG_GST_DEFAULT_ELEMENTS + +AC_DEFUN([AG_GST_DEFAULT_ELEMENTS], +[ + dnl decide on default elements + dnl FIXME: describe where exactly this gets used + dnl FIXME: decide if it's a problem that this could point to sinks from + dnl depending plugin modules + dnl FIXME: when can we just use autoaudiosrc and autovideosrc? + DEFAULT_AUDIOSINK="autoaudiosink" + DEFAULT_VIDEOSINK="autovideosink" + DEFAULT_AUDIOSRC="alsasrc" + DEFAULT_VIDEOSRC="v4l2src" + DEFAULT_VISUALIZER="goom" + case "$host" in + *-sun-* | *pc-solaris* ) + DEFAULT_AUDIOSRC="sunaudiosrc" + ;; + *-darwin* ) + DEFAULT_AUDIOSRC="osxaudiosrc" + ;; + esac + + dnl Default audio sink + AC_ARG_WITH(default-audiosink, + AC_HELP_STRING([--with-default-audiosink], [specify default audio sink]), + [ + case "${withval}" in + yes) AC_MSG_ERROR(bad value ${withval} for --with-default-audiosink) ;; + no) AC_MSG_ERROR(bad value ${withval} for --with-default-audiosink) ;; + *) DEFAULT_AUDIOSINK="${withval}" ;; + esac + ], + [ + DEFAULT_AUDIOSINK="$DEFAULT_AUDIOSINK" + ] dnl Default value as determined above + ) + AC_MSG_NOTICE(Using $DEFAULT_AUDIOSINK as default audio sink) + AC_SUBST(DEFAULT_AUDIOSINK) + AC_DEFINE_UNQUOTED(DEFAULT_AUDIOSINK, "$DEFAULT_AUDIOSINK", + [Default audio sink]) + + dnl Default audio source + AC_ARG_WITH(default-audiosrc, + AC_HELP_STRING([--with-default-audiosrc], [specify default audio source]), + [ + case "${withval}" in + yes) AC_MSG_ERROR(bad value ${withval} for --with-default-audiosrc) ;; + no) AC_MSG_ERROR(bad value ${withval} for --with-default-audiosrc) ;; + *) DEFAULT_AUDIOSRC="${withval}" ;; + esac + ], + [ + DEFAULT_AUDIOSRC="$DEFAULT_AUDIOSRC" + ] dnl Default value as determined above + ) + AC_MSG_NOTICE(Using $DEFAULT_AUDIOSRC as default audio source) + AC_SUBST(DEFAULT_AUDIOSRC) + AC_DEFINE_UNQUOTED(DEFAULT_AUDIOSRC, "$DEFAULT_AUDIOSRC", + [Default audio source]) + + dnl Default video sink + AC_ARG_WITH(default-videosink, + AC_HELP_STRING([--with-default-videosink], [specify default video sink]), + [ + case "${withval}" in + yes) AC_MSG_ERROR(bad value ${withval} for --with-default-videosink) ;; + no) AC_MSG_ERROR(bad value ${withval} for --with-default-videosink) ;; + *) DEFAULT_VIDEOSINK="${withval}" ;; + esac + ], + [ + DEFAULT_VIDEOSINK="$DEFAULT_VIDEOSINK" + ] dnl Default value as determined above + ) + AC_MSG_NOTICE(Using $DEFAULT_VIDEOSINK as default video sink) + AC_SUBST(DEFAULT_VIDEOSINK) + AC_DEFINE_UNQUOTED(DEFAULT_VIDEOSINK, "$DEFAULT_VIDEOSINK", + [Default video sink]) + + dnl Default video source + AC_ARG_WITH(default-videosrc, + AC_HELP_STRING([--with-default-videosrc], [specify default video source]), + [ + case "${withval}" in + yes) AC_MSG_ERROR(bad value ${withval} for --with-default-videosrc) ;; + no) AC_MSG_ERROR(bad value ${withval} for --with-default-videosrc) ;; + *) DEFAULT_VIDEOSRC="${withval}" ;; + esac + ], + [ + DEFAULT_VIDEOSRC="$DEFAULT_VIDEOSRC" + ] dnl Default value as determined above + ) + AC_MSG_NOTICE(Using $DEFAULT_VIDEOSRC as default video source) + AC_SUBST(DEFAULT_VIDEOSRC) + AC_DEFINE_UNQUOTED(DEFAULT_VIDEOSRC, "$DEFAULT_VIDEOSRC", + [Default video source]) + + dnl Default visualizer + AC_ARG_WITH(default-visualizer, + AC_HELP_STRING([--with-default-visualizer], [specify default visualizer]), + [ + case "${withval}" in + yes) AC_MSG_ERROR(bad value ${withval} for --with-default-visualizer) ;; + no) AC_MSG_ERROR(bad value ${withval} for --with-default-visualizer) ;; + *) DEFAULT_VISUALIZER="${withval}" ;; + esac + ], + [ + DEFAULT_VISUALIZER="$DEFAULT_VISUALIZER" + ] dnl Default value as determined above + ) + AC_MSG_NOTICE(Using $DEFAULT_VISUALIZER as default visualizer) + AC_SUBST(DEFAULT_VISUALIZER) + AC_DEFINE_UNQUOTED(DEFAULT_VISUALIZER, "$DEFAULT_VISUALIZER", + [Default visualizer]) +]) diff --git a/validate/common/m4/gst-doc.m4 b/validate/common/m4/gst-doc.m4 new file mode 100644 index 0000000..5d3f0fd --- /dev/null +++ b/validate/common/m4/gst-doc.m4 @@ -0,0 +1,92 @@ +AC_DEFUN([AG_GST_DOCBOOK_CHECK], +[ + dnl choose a location to install docbook docs in + if test "x$PACKAGE_TARNAME" = "x" + then + AC_MSG_ERROR([Internal error - PACKAGE_TARNAME not set]) + fi + docdir="\$(datadir)/doc/$PACKAGE_TARNAME-$GST_API_VERSION" + + dnl enable/disable docbook documentation building + AC_ARG_ENABLE(docbook, + AC_HELP_STRING([--enable-docbook], + [use docbook to build documentation [default=no]]),, + enable_docbook=no) + + have_docbook=no + + if test x$enable_docbook = xyes; then + dnl check if we actually have everything we need + + dnl check for docbook tools + AC_CHECK_PROG(HAVE_DOCBOOK2PS, docbook2ps, yes, no) + AC_CHECK_PROG(HAVE_XSLTPROC, xsltproc, yes, no) + AC_CHECK_PROG(HAVE_JADETEX, jadetex, yes, no) + AC_CHECK_PROG(HAVE_PS2PDF, ps2pdf, yes, no) + + dnl check if we can process docbook stuff + AS_DOCBOOK(have_docbook=yes, have_docbook=no) + + dnl check for extra tools + AC_CHECK_PROG(HAVE_DVIPS, dvips, yes, no) + AC_CHECK_PROG(HAVE_XMLLINT, xmllint, yes, no) + + AC_CHECK_PROG(HAVE_PNGTOPNM, pngtopnm, yes, no) + AC_CHECK_PROG(HAVE_PNMTOPS, pnmtops, yes, no) + AC_CHECK_PROG(HAVE_EPSTOPDF, epstopdf, yes, no) + + dnl check if we can generate HTML + if test "x$HAVE_XSLTPROC" = "xyes" && \ + test "x$enable_docbook" = "xyes" && \ + test "x$HAVE_XMLLINT" = "xyes"; then + DOC_HTML=yes + AC_MSG_NOTICE(Will output HTML documentation) + else + DOC_HTML=no + AC_MSG_NOTICE(Will not output HTML documentation) + fi + + dnl check if we can generate PS + if test "x$HAVE_DOCBOOK2PS" = "xyes" && \ + test "x$enable_docbook" = "xyes" && \ + test "x$HAVE_XMLLINT" = "xyes" && \ + test "x$HAVE_JADETEX" = "xyes" && \ + test "x$HAVE_DVIPS" = "xyes" && \ + test "x$HAVE_PNGTOPNM" = "xyes" && \ + test "x$HAVE_PNMTOPS" = "xyes"; then + DOC_PS=yes + AC_MSG_NOTICE(Will output PS documentation) + else + DOC_PS=no + AC_MSG_NOTICE(Will not output PS documentation) + fi + + dnl check if we can generate PDF - using only ps2pdf + if test "x$DOC_PS" = "xyes" && \ + test "x$enable_docbook" = "xyes" && \ + test "x$HAVE_XMLLINT" = "xyes" && \ + test "x$HAVE_PS2PDF" = "xyes"; then + DOC_PDF=yes + AC_MSG_NOTICE(Will output PDF documentation) + else + DOC_PDF=no + AC_MSG_NOTICE(Will not output PDF documentation) + fi + + dnl if we don't have everything, we should disable + if test "x$have_docbook" != "xyes"; then + enable_docbook=no + fi + fi + + dnl if we're going to install documentation, tell us where + if test "x$have_docbook" = "xyes"; then + AC_MSG_NOTICE(Installing documentation in $docdir) + AC_SUBST(docdir) + fi + + AM_CONDITIONAL(ENABLE_DOCBOOK, test x$enable_docbook = xyes) + AM_CONDITIONAL(DOC_HTML, test x$DOC_HTML = xyes) + AM_CONDITIONAL(DOC_PDF, test x$DOC_PDF = xyes) + AM_CONDITIONAL(DOC_PS, test x$DOC_PS = xyes) +]) diff --git a/validate/common/m4/gst-dowhile.m4 b/validate/common/m4/gst-dowhile.m4 new file mode 100644 index 0000000..069808d --- /dev/null +++ b/validate/common/m4/gst-dowhile.m4 @@ -0,0 +1,24 @@ +dnl +dnl Check for working do while(0) macros. This is used by G_STMT_START +dnl and G_STMT_END in glib/gmacros.h. Without having this defined we +dnl get "ambigious if-else" compiler warnings when compling C++ code. +dnl +dnl Copied from GLib's configure.in +dnl +AC_DEFUN([AG_GST_CHECK_DOWHILE_MACROS],[ + +dnl *** check for working do while(0) macros *** +AC_CACHE_CHECK([for working do while(0) macros], _cv_g_support_dowhile_macros, [ + AC_TRY_COMPILE([],[ + #define STMT_START do + #define STMT_END while(0) + #define STMT_TEST STMT_START { i = 0; } STMT_END + int main(void) { int i = 1; STMT_TEST; return i; }], + [_cv_g_support_dowhile_macros=yes], + [_cv_g_support_dowhile_macros=no], + [_cv_g_support_dowhile_macros=yes]) +]) +if test x$_cv_g_support_dowhile_macros = xyes; then + AC_DEFINE(HAVE_DOWHILE_MACROS, 1, [define for working do while(0) macros]) +fi +]) diff --git a/validate/common/m4/gst-error.m4 b/validate/common/m4/gst-error.m4 new file mode 100644 index 0000000..d6487cc --- /dev/null +++ b/validate/common/m4/gst-error.m4 @@ -0,0 +1,290 @@ +dnl handle various error-related things + +dnl Thomas Vander Stichele +dnl Tim-Philipp Müller + +dnl Last modification: 2008-02-18 + +dnl AG_GST_SET_ERROR_CFLAGS([ADD-WERROR], [MORE_FLAGS]) +dnl AG_GST_SET_ERROR_CXXFLAGS([ADD-WERROR], [MORE_FLAGS]) +dnl AG_GST_SET_LEVEL_DEFAULT([IS-GIT-VERSION]) + + +dnl Sets WARNING_CFLAGS and ERROR_CFLAGS to something the compiler +dnl will accept and AC_SUBST them so they are available in Makefile +dnl +dnl WARNING_CFLAGS will contain flags to make the compiler emit more +dnl warnings. +dnl ERROR_CFLAGS will contain flags to make those warnings fatal, +dnl unless ADD-WERROR is set to "no" +dnl +dnl If MORE_FLAGS is set, tries to add each of the given flags +dnl to WARNING_CFLAGS if the compiler supports them. Each flag is +dnl tested separately. +dnl +dnl These flags can be overridden at make time: +dnl make ERROR_CFLAGS= +AC_DEFUN([AG_GST_SET_ERROR_CFLAGS], +[ + AC_REQUIRE([AC_PROG_CC]) + AC_REQUIRE([AS_COMPILER_FLAG]) + + WARNING_CFLAGS="" + ERROR_CFLAGS="" + + dnl if we support -Wall, set it unconditionally + AS_COMPILER_FLAG(-Wall, + WARNING_CFLAGS="$WARNING_CFLAGS -Wall") + + dnl Warn if declarations after statements are used (C99 extension) + AS_COMPILER_FLAG(-Wdeclaration-after-statement, + WARNING_CFLAGS="$WARNING_CFLAGS -Wdeclaration-after-statement") + + dnl Warn if variable length arrays are used (C99 extension) + AS_COMPILER_FLAG(-Wvla, + WARNING_CFLAGS="$WARNING_CFLAGS -Wvla") + + dnl Warn for invalid pointer arithmetic + AS_COMPILER_FLAG(-Wpointer-arith, + WARNING_CFLAGS="$WARNING_CFLAGS -Wpointer-arith") + + dnl if asked for, add -Werror if supported + if test "x$1" != "xno" + then + AS_COMPILER_FLAG(-Werror, ERROR_CFLAGS="$ERROR_CFLAGS -Werror") + + dnl if -Werror isn't suported, try -errwarn=%all (Sun Forte case) + if test "x$ERROR_CFLAGS" = "x" + then + AS_COMPILER_FLAG([-errwarn=%all], [ + ERROR_CFLAGS="-errwarn=%all" + dnl try -errwarn=%all,no%E_EMPTY_DECLARATION, + dnl no%E_STATEMENT_NOT_REACHED,no%E_ARGUEMENT_MISMATCH, + dnl no%E_MACRO_REDEFINED (Sun Forte case) + dnl For Forte we need disable "empty declaration" warning produced by un-needed semicolon + dnl "statement not reached" disabled because there is g_assert_not_reached () in some places + dnl "macro redefined" because of gst/gettext.h + dnl FIXME: is it really supposed to be 'ARGUEMENT' and not 'ARGUMENT'? + for f in 'no%E_EMPTY_DECLARATION' \ + 'no%E_STATEMENT_NOT_REACHED' \ + 'no%E_ARGUEMENT_MISMATCH' \ + 'no%E_MACRO_REDEFINED' \ + 'no%E_LOOP_NOT_ENTERED_AT_TOP' + do + AS_COMPILER_FLAG([-errwarn=%all,$f], [ + ERROR_CFLAGS="$ERROR_CFLAGS,$f" + ]) + done + ]) + fi + fi + + if test "x$2" != "x" + then + UNSUPPORTED="" + list="$2" + for each in $list + do + AS_COMPILER_FLAG($each, + WARNING_CFLAGS="$WARNING_CFLAGS $each", + UNSUPPORTED="$UNSUPPORTED $each") + done + if test "X$UNSUPPORTED" != X ; then + AC_MSG_NOTICE([unsupported compiler flags: $UNSUPPORTED]) + fi + fi + + AC_SUBST(WARNING_CFLAGS) + AC_SUBST(ERROR_CFLAGS) + AC_MSG_NOTICE([set WARNING_CFLAGS to $WARNING_CFLAGS]) + AC_MSG_NOTICE([set ERROR_CFLAGS to $ERROR_CFLAGS]) +]) + +dnl Sets WARNING_CXXFLAGS and ERROR_CXXFLAGS to something the compiler +dnl will accept and AC_SUBST them so they are available in Makefile +dnl +dnl WARNING_CXXFLAGS will contain flags to make the compiler emit more +dnl warnings. +dnl ERROR_CXXFLAGS will contain flags to make those warnings fatal, +dnl unless ADD-WERROR is set to "no" +dnl +dnl If MORE_FLAGS is set, tries to add each of the given flags +dnl to WARNING_CFLAGS if the compiler supports them. Each flag is +dnl tested separately. +dnl +dnl These flags can be overridden at make time: +dnl make ERROR_CXXFLAGS= +AC_DEFUN([AG_GST_SET_ERROR_CXXFLAGS], +[ + AC_REQUIRE([AC_PROG_CXX]) + AC_REQUIRE([AS_CXX_COMPILER_FLAG]) + + ERROR_CXXFLAGS="" + WARNING_CXXFLAGS="" + + dnl if we support -Wall, set it unconditionally + AS_CXX_COMPILER_FLAG(-Wall, WARNING_CXXFLAGS="$WARNING_CXXFLAGS -Wall") + + dnl if asked for, add -Werror if supported + if test "x$1" != "xno" + then + AS_CXX_COMPILER_FLAG(-Werror, ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror") + + if test "x$ERROR_CXXFLAGS" != "x" + then + dnl add exceptions + AS_CXX_COMPILER_FLAG([-Wno-non-virtual-dtor], ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Wno-non-virtual-dtor") + + dnl Add -fno-strict-aliasing for GLib versions before 2.19.8 + dnl as before G_LOCK and friends caused strict aliasing compiler + dnl warnings. + PKG_CHECK_EXISTS([glib-2.0 < 2.19.8], [ + AS_CXX_COMPILER_FLAG([-fno-strict-aliasing], + ERROR_CXXFLAGS="$ERROR_CXXFLAGS -fno-strict-aliasing") + ]) + else + dnl if -Werror isn't suported, try -errwarn=%all + AS_CXX_COMPILER_FLAG([-errwarn=%all], ERROR_CXXFLAGS="$ERROR_CXXFLAGS -errwarn=%all") + if test "x$ERROR_CXXFLAGS" != "x"; then + dnl try -errwarn=%all,no%E_EMPTY_DECLARATION, + dnl no%E_STATEMENT_NOT_REACHED,no%E_ARGUEMENT_MISMATCH, + dnl no%E_MACRO_REDEFINED (Sun Forte case) + dnl For Forte we need disable "empty declaration" warning produced by un-needed semicolon + dnl "statement not reached" disabled because there is g_assert_not_reached () in some places + dnl "macro redefined" because of gst/gettext.h + dnl FIXME: is it really supposed to be 'ARGUEMENT' and not 'ARGUMENT'? + dnl FIXME: do any of these work with the c++ compiler? if not, why + dnl do we check at all? + for f in 'no%E_EMPTY_DECLARATION' \ + 'no%E_STATEMENT_NOT_REACHED' \ + 'no%E_ARGUEMENT_MISMATCH' \ + 'no%E_MACRO_REDEFINED' \ + 'no%E_LOOP_NOT_ENTERED_AT_TOP' + do + AS_CXX_COMPILER_FLAG([-errwarn=%all,$f], [ERROR_CXXFLAGS="$ERROR_CXXFLAGS,$f"]) + done + fi + fi + fi + + if test "x$2" != "x" + then + UNSUPPORTED="" + list="$2" + for each in $list + do + AS_CXX_COMPILER_FLAG($each, + WARNING_CXXFLAGS="$WARNING_CXXFLAGS $each", + UNSUPPORTED="$UNSUPPORTED $each") + done + if test "X$UNSUPPORTED" != X ; then + AC_MSG_NOTICE([unsupported compiler flags: $UNSUPPORTED]) + fi + fi + + AC_SUBST(WARNING_CXXFLAGS) + AC_SUBST(ERROR_CXXFLAGS) + AC_MSG_NOTICE([set WARNING_CXXFLAGS to $WARNING_CXXFLAGS]) + AC_MSG_NOTICE([set ERROR_CXXFLAGS to $ERROR_CXXFLAGS]) +]) + +dnl Sets WARNING_OBJCFLAGS and ERROR_OBJCFLAGS to something the compiler +dnl will accept and AC_SUBST them so they are available in Makefile +dnl +dnl WARNING_OBJCFLAGS will contain flags to make the compiler emit more +dnl warnings. +dnl ERROR_OBJCFLAGS will contain flags to make those warnings fatal, +dnl unless ADD-WERROR is set to "no" +dnl +dnl If MORE_FLAGS is set, tries to add each of the given flags +dnl to WARNING_CFLAGS if the compiler supports them. Each flag is +dnl tested separately. +dnl +dnl These flags can be overridden at make time: +dnl make ERROR_OBJCFLAGS= +AC_DEFUN([AG_GST_SET_ERROR_OBJCFLAGS], +[ + AC_REQUIRE([AC_PROG_OBJC]) + AC_REQUIRE([AS_OBJC_COMPILER_FLAG]) + + ERROR_OBJCFLAGS="" + WARNING_OBJCFLAGS="" + + dnl if we support -Wall, set it unconditionally + AS_OBJC_COMPILER_FLAG(-Wall, WARNING_OBJCFLAGS="$WARNING_OBJCFLAGS -Wall") + + dnl if asked for, add -Werror if supported + if test "x$1" != "xno" + then + AS_OBJC_COMPILER_FLAG(-Werror, ERROR_OBJCFLAGS="$ERROR_OBJCFLAGS -Werror") + + if test "x$ERROR_OBJCFLAGS" != "x" + then + dnl Add -fno-strict-aliasing for GLib versions before 2.19.8 + dnl as before G_LOCK and friends caused strict aliasing compiler + dnl warnings. + PKG_CHECK_EXISTS([glib-2.0 < 2.19.8], [ + AS_OBJC_COMPILER_FLAG([-fno-strict-aliasing], + ERROR_OBJCFLAGS="$ERROR_OBJCFLAGS -fno-strict-aliasing") + ]) + else + dnl if -Werror isn't suported, try -errwarn=%all + AS_OBJC_COMPILER_FLAG([-errwarn=%all], ERROR_OBJCFLAGS="$ERROR_OBJCFLAGS -errwarn=%all") + if test "x$ERROR_OBJCFLAGS" != "x"; then + dnl try -errwarn=%all,no%E_EMPTY_DECLARATION, + dnl no%E_STATEMENT_NOT_REACHED,no%E_ARGUEMENT_MISMATCH, + dnl no%E_MACRO_REDEFINED (Sun Forte case) + dnl For Forte we need disable "empty declaration" warning produced by un-needed semicolon + dnl "statement not reached" disabled because there is g_assert_not_reached () in some places + dnl "macro redefined" because of gst/gettext.h + dnl FIXME: is it really supposed to be 'ARGUEMENT' and not 'ARGUMENT'? + dnl FIXME: do any of these work with the c++ compiler? if not, why + dnl do we check at all? + for f in 'no%E_EMPTY_DECLARATION' \ + 'no%E_STATEMENT_NOT_REACHED' \ + 'no%E_ARGUEMENT_MISMATCH' \ + 'no%E_MACRO_REDEFINED' \ + 'no%E_LOOP_NOT_ENTERED_AT_TOP' + do + AS_OBJC_COMPILER_FLAG([-errwarn=%all,$f], [ERROR_OBJCFLAGS="$ERROR_OBJCFLAGS,$f"]) + done + fi + fi + fi + + if test "x$2" != "x" + then + UNSUPPORTED="" + list="$2" + for each in $list + do + AS_OBJC_COMPILER_FLAG($each, + WARNING_OBJCFLAGS="$WARNING_OBJCFLAGS $each", + UNSUPPORTED="$UNSUPPORTED $each") + done + if test "X$UNSUPPORTED" != X ; then + AC_MSG_NOTICE([unsupported compiler flags: $UNSUPPORTED]) + fi + fi + + AC_SUBST(WARNING_OBJCFLAGS) + AC_SUBST(ERROR_OBJCFLAGS) + AC_MSG_NOTICE([set WARNING_OBJCFLAGS to $WARNING_OBJCFLAGS]) + AC_MSG_NOTICE([set ERROR_OBJCFLAGS to $ERROR_OBJCFLAGS]) +]) + +dnl Sets the default error level for debugging messages +AC_DEFUN([AG_GST_SET_LEVEL_DEFAULT], +[ + dnl define correct errorlevel for debugging messages. We want to have + dnl GST_ERROR messages printed when running cvs builds + if test "x[$1]" = "xyes"; then + GST_LEVEL_DEFAULT=GST_LEVEL_ERROR + else + GST_LEVEL_DEFAULT=GST_LEVEL_NONE + fi + AC_DEFINE_UNQUOTED(GST_LEVEL_DEFAULT, $GST_LEVEL_DEFAULT, + [Default errorlevel to use]) + dnl AC_SUBST so we can use it for win32/common/config.h + AC_SUBST(GST_LEVEL_DEFAULT) +]) diff --git a/validate/common/m4/gst-feature.m4 b/validate/common/m4/gst-feature.m4 new file mode 100644 index 0000000..876215e --- /dev/null +++ b/validate/common/m4/gst-feature.m4 @@ -0,0 +1,297 @@ +dnl Perform a check for a feature for GStreamer +dnl Richard Boulton +dnl Thomas Vander Stichele added useful stuff +dnl Last modification: 25/06/2001 +dnl +dnl AG_GST_CHECK_FEATURE(FEATURE-NAME, FEATURE-DESCRIPTION, +dnl DEPENDENT-PLUGINS, TEST-FOR-FEATURE, +dnl DISABLE-BY-DEFAULT, ACTION-IF-USE, ACTION-IF-NOTUSE) +dnl +dnl This macro adds a command line argument to allow the user to enable +dnl or disable a feature, and if the feature is enabled, performs a supplied +dnl test to check if the feature is available. +dnl +dnl The test should define HAVE_ to "yes" or "no" depending +dnl on whether the feature is available. +dnl +dnl The macro will set USE_ to "yes" or "no" depending on +dnl whether the feature is to be used. +dnl Thomas changed this, so that when USE_ was already set +dnl to no, then it stays that way. +dnl +dnl The macro will call AM_CONDITIONAL(USE_, ...) to allow +dnl the feature to control what is built in Makefile.ams. If you want +dnl additional actions resulting from the test, you can add them with the +dnl ACTION-IF-USE and ACTION-IF-NOTUSE parameters. +dnl +dnl FEATURE-NAME is the name of the feature, and should be in +dnl purely upper case characters. +dnl FEATURE-DESCRIPTION is used to describe the feature in help text for +dnl the command line argument. +dnl DEPENDENT-PLUGINS lists any plug-ins which depend on this feature. +dnl TEST-FOR-FEATURE is a test which sets HAVE_ to "yes" +dnl or "no" depending on whether the feature is +dnl available. +dnl DISABLE-BY-DEFAULT if "disabled", the feature is disabled by default, +dnl if any other value, the feature is enabled by default. +dnl ACTION-IF-USE any extra actions to perform if the feature is to be +dnl used. +dnl ACTION-IF-NOTUSE any extra actions to perform if the feature is not to +dnl be used. +dnl +dnl +dnl thomas : +dnl we also added a history. +dnl GST_PLUGINS_YES will contain all plugins to be built +dnl that were checked through AG_GST_CHECK_FEATURE +dnl GST_PLUGINS_NO will contain those that won't be built + +AC_DEFUN([AG_GST_CHECK_FEATURE], +[echo +AC_MSG_NOTICE(*** checking feature: [$2] ***) +if test "x[$3]" != "x" +then + AC_MSG_NOTICE(*** for plug-ins: [$3] ***) +fi +dnl +builtin(define, [gst_endisable], ifelse($5, [disabled], [enable], [disable]))dnl +dnl if it is set to NO, then don't even consider it for building +NOUSE= +if test "x$USE_[$1]" = "xno"; then + NOUSE="yes" +fi +AC_ARG_ENABLE(translit([$1], A-Z, a-z), + [ ]builtin(format, --%-26s gst_endisable %s, gst_endisable-translit([$1], A-Z, a-z), [$2]ifelse([$3],,,: [$3])), + [ case "${enableval}" in + yes) USE_[$1]=yes;; + no) USE_[$1]=no;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-translit([$1], A-Z, a-z)) ;; + esac], + [ USE_$1=]ifelse($5, [disabled], [no], [yes])) dnl DEFAULT + +dnl *** set it back to no if it was preset to no +if test "x$NOUSE" = "xyes"; then + USE_[$1]="no" + AC_MSG_WARN(*** $3 pre-configured not to be built) +fi +NOUSE= + +dnl *** Check if it is ported or not +if echo " [$GST_PLUGINS_NONPORTED] " | tr , ' ' | grep -i " [$1] " > /dev/null; then + USE_[$1]="no" + AC_MSG_WARN(*** $3 not ported) +fi + +dnl *** If it's enabled + +if test x$USE_[$1] = xyes; then + dnl save compile variables before the test + + gst_check_save_LIBS=$LIBS + gst_check_save_LDFLAGS=$LDFLAGS + gst_check_save_CFLAGS=$CFLAGS + gst_check_save_CPPFLAGS=$CPPFLAGS + gst_check_save_CXXFLAGS=$CXXFLAGS + + HAVE_[$1]=no + dnl TEST_FOR_FEATURE + $4 + + LIBS=$gst_check_save_LIBS + LDFLAGS=$gst_check_save_LDFLAGS + CFLAGS=$gst_check_save_CFLAGS + CPPFLAGS=$gst_check_save_CPPFLAGS + CXXFLAGS=$gst_check_save_CXXFLAGS + + dnl If it isn't found, unset USE_[$1] + if test x$HAVE_[$1] = xno; then + USE_[$1]=no + else + ifelse([$3], , :, [AC_MSG_NOTICE(*** These plugins will be built: [$3])]) + fi +fi +dnl *** Warn if it's disabled or not found +if test x$USE_[$1] = xyes; then + ifelse([$6], , :, [$6]) + if test "x$3" != "x"; then + GST_PLUGINS_YES="\t[$3]\n$GST_PLUGINS_YES" + fi + AC_DEFINE(HAVE_[$1], , [Define to enable $2]ifelse($3,,, [ (used by $3)]).) +else + ifelse([$3], , :, [AC_MSG_NOTICE(*** These plugins will not be built: [$3])]) + if test "x$3" != "x"; then + GST_PLUGINS_NO="\t[$3]\n$GST_PLUGINS_NO" + fi + ifelse([$7], , :, [$7]) +fi +dnl *** Define the conditional as appropriate +AM_CONDITIONAL(USE_[$1], test x$USE_[$1] = xyes) +]) + +dnl Use AC_CHECK_LIB and AC_CHECK_HEADER to do both tests at once +dnl sets HAVE_module if we have it +dnl Richard Boulton +dnl Last modification: 26/06/2001 +dnl AG_GST_CHECK_LIBHEADER(FEATURE-NAME, LIB NAME, LIB FUNCTION, EXTRA LD FLAGS, +dnl HEADER NAME, ACTION-IF-FOUND, ACTION-IF-NOT-FOUND) +dnl +dnl This check was written for GStreamer: it should be renamed and checked +dnl for portability if you decide to use it elsewhere. +dnl +AC_DEFUN([AG_GST_CHECK_LIBHEADER], +[ + AC_CHECK_LIB([$2], [$3], HAVE_[$1]=yes, HAVE_[$1]=no,[$4]) + if test "x$HAVE_[$1]" = "xyes"; then + AC_CHECK_HEADER([$5], :, HAVE_[$1]=no) + if test "x$HAVE_[$1]" = "xyes"; then + dnl execute what needs to be + ifelse([$6], , :, [$6]) + else + ifelse([$7], , :, [$7]) + fi + else + ifelse([$7], , :, [$7]) + fi + AC_SUBST(HAVE_[$1]) +] +) + +dnl 2004-02-14 Thomas - changed to get set properly and use proper output +dnl 2003-06-27 Benjamin Otte - changed to make this work with gstconfig.h +dnl +dnl Add a subsystem --disable flag and all the necessary symbols and substitions +dnl +dnl AG_GST_CHECK_SUBSYSTEM_DISABLE(SYSNAME, [subsystem name]) +dnl +AC_DEFUN([AG_GST_CHECK_SUBSYSTEM_DISABLE], +[ + dnl this define will replace each literal subsys_def occurrence with + dnl the lowercase hyphen-separated subsystem + dnl e.g. if $1 is GST_DEBUG then subsys_def will be a macro with gst-debug + define([subsys_def],translit([$1], _A-Z, -a-z)) + + AC_ARG_ENABLE(subsys_def, + AC_HELP_STRING(--disable-subsys_def, [disable $2]), + [ + case "${enableval}" in + yes) GST_DISABLE_[$1]=no ;; + no) GST_DISABLE_[$1]=yes ;; + *) AC_MSG_ERROR([bad value ${enableval} for --enable-subsys_def]) ;; + esac + ], + [GST_DISABLE_[$1]=no]) dnl Default value + + if test x$GST_DISABLE_[$1] = xyes; then + AC_MSG_NOTICE([disabled subsystem [$2]]) + GST_DISABLE_[$1]_DEFINE="#define GST_DISABLE_$1 1" + else + GST_DISABLE_[$1]_DEFINE="/* #undef GST_DISABLE_$1 */" + fi + AC_SUBST(GST_DISABLE_[$1]_DEFINE) + undefine([subsys_def]) +]) + + +dnl Parse gstconfig.h for feature and defines add the symbols and substitions +dnl +dnl AG_GST_PARSE_SUBSYSTEM_DISABLE(GST_CONFIGPATH, FEATURE) +dnl +AC_DEFUN([AG_GST_PARSE_SUBSYSTEM_DISABLE], +[ + grep >/dev/null "#undef GST_DISABLE_$2" $1 + if test $? = 0; then + GST_DISABLE_[$2]=0 + else + GST_DISABLE_[$2]=1 + fi + AC_SUBST(GST_DISABLE_[$2]) +]) + +dnl Parse gstconfig.h and defines add the symbols and substitions +dnl +dnl GST_CONFIGPATH=`$PKG_CONFIG --variable=includedir gstreamer-1.0`"/gst/gstconfig.h" +dnl AG_GST_PARSE_SUBSYSTEM_DISABLES(GST_CONFIGPATH) +dnl +AC_DEFUN([AG_GST_PARSE_SUBSYSTEM_DISABLES], +[ + AG_GST_PARSE_SUBSYSTEM_DISABLE($1,GST_DEBUG) + AG_GST_PARSE_SUBSYSTEM_DISABLE($1,LOADSAVE) + AG_GST_PARSE_SUBSYSTEM_DISABLE($1,PARSE) + AG_GST_PARSE_SUBSYSTEM_DISABLE($1,TRACE) + AG_GST_PARSE_SUBSYSTEM_DISABLE($1,ALLOC_TRACE) + AG_GST_PARSE_SUBSYSTEM_DISABLE($1,REGISTRY) + AG_GST_PARSE_SUBSYSTEM_DISABLE($1,PLUGIN) + AG_GST_PARSE_SUBSYSTEM_DISABLE($1,XML) +]) + +dnl AG_GST_CHECK_GST_DEBUG_DISABLED(ACTION-IF-DISABLED, ACTION-IF-NOT-DISABLED) +dnl +dnl Checks if the GStreamer debugging system is disabled in the core version +dnl we are compiling against (by checking gstconfig.h) +dnl +AC_DEFUN([AG_GST_CHECK_GST_DEBUG_DISABLED], +[ + AC_REQUIRE([AG_GST_CHECK_GST]) + + AC_MSG_CHECKING([whether the GStreamer debugging system is enabled]) + AC_LANG_PUSH([C]) + save_CFLAGS="$CFLAGS" + CFLAGS="$GST_CFLAGS $CFLAGS" + AC_COMPILE_IFELSE([ + AC_LANG_SOURCE([[ + #include + #ifdef GST_DISABLE_GST_DEBUG + #error "debugging disabled, make compiler fail" + #endif]])], [ debug_system_enabled=yes], [debug_system_enabled=no]) + CFLAGS="$save_CFLAGS" + AC_LANG_POP([C]) + + AC_MSG_RESULT([$debug_system_enabled]) + + if test "x$debug_system_enabled" = "xyes" ; then + $2 + true + else + $1 + true + fi +]) + +dnl relies on GST_PLUGINS_ALL, GST_PLUGINS_SELECTED, GST_PLUGINS_YES, +dnl GST_PLUGINS_NO, and BUILD_EXTERNAL +AC_DEFUN([AG_GST_OUTPUT_PLUGINS], [ + +printf "configure: *** Plug-ins without external dependencies that will be built:\n" +( for i in $GST_PLUGINS_SELECTED; do printf '\t'$i'\n'; done ) | sort +printf "\n" + +printf "configure: *** Plug-ins without external dependencies that will NOT be built:\n" +( for i in $GST_PLUGINS_ALL; do + case " $GST_PLUGINS_SELECTED " in + *\ $i\ *) + ;; + *) + printf '\t'$i'\n' + ;; + esac + done ) | sort +printf "\n" + +printf "configure: *** Plug-ins that have NOT been ported:\n" +( for i in $GST_PLUGINS_NONPORTED; do + printf '\t'$i'\n' + done ) | sort +printf "\n" + +if test "x$BUILD_EXTERNAL" = "xno"; then + printf "configure: *** No plug-ins with external dependencies will be built\n" +else + printf "configure: *** Plug-ins with dependencies that will be built:" + printf "$GST_PLUGINS_YES\n" | sort + printf "\n" + printf "configure: *** Plug-ins with dependencies that will NOT be built:" + printf "$GST_PLUGINS_NO\n" | sort + printf "\n" +fi +]) + diff --git a/validate/common/m4/gst-function.m4 b/validate/common/m4/gst-function.m4 new file mode 100644 index 0000000..61adfd3 --- /dev/null +++ b/validate/common/m4/gst-function.m4 @@ -0,0 +1,63 @@ +dnl +dnl Check for compiler mechanism to show functions in debugging +dnl copied from an Ali patch floating on the internet +dnl +AC_DEFUN([AG_GST_CHECK_FUNCTION],[ + dnl #1: __PRETTY_FUNCTION__ + AC_MSG_CHECKING(whether $CC implements __PRETTY_FUNCTION__) + AC_CACHE_VAL(gst_cv_have_pretty_function,[ + AC_TRY_LINK([#include ], + [printf("%s", __PRETTY_FUNCTION__);], + gst_cv_have_pretty_function=yes, + gst_cv_have_pretty_function=no) + ]) + AC_MSG_RESULT($gst_cv_have_pretty_function) + if test "$gst_cv_have_pretty_function" = yes; then + AC_DEFINE(HAVE_PRETTY_FUNCTION, 1, + [defined if the compiler implements __PRETTY_FUNCTION__]) + fi + +dnl #2: __FUNCTION__ + AC_MSG_CHECKING(whether $CC implements __FUNCTION__) + AC_CACHE_VAL(gst_cv_have_function,[ + AC_TRY_LINK([#include ], + [printf("%s", __FUNCTION__);], + gst_cv_have_function=yes, + gst_cv_have_function=no) + ]) + AC_MSG_RESULT($gst_cv_have_function) + if test "$gst_cv_have_function" = yes; then + AC_DEFINE(HAVE_FUNCTION, 1, + [defined if the compiler implements __FUNCTION__]) + fi + +dnl #3: __func__ + AC_MSG_CHECKING(whether $CC implements __func__) + AC_CACHE_VAL(gst_cv_have_func,[ + AC_TRY_LINK([#include ], + [printf("%s", __func__);], + gst_cv_have_func=yes, + gst_cv_have_func=no) + ]) + AC_MSG_RESULT($gst_cv_have_func) + if test "$gst_cv_have_func" = yes; then + AC_DEFINE(HAVE_FUNC, 1, + [defined if the compiler implements __func__]) + fi + +dnl now define FUNCTION to whatever works, and fallback to "" + if test "$gst_cv_have_pretty_function" = yes; then + function=__PRETTY_FUNCTION__ + else + if test "$gst_cv_have_function" = yes; then + function=__FUNCTION__ + else + if test "$gst_cv_have_func" = yes; then + function=__func__ + else + function=\"\" + fi + fi + fi + AC_DEFINE_UNQUOTED(GST_FUNCTION, $function, [macro to use to show function name]) +]) diff --git a/validate/common/m4/gst-gettext.m4 b/validate/common/m4/gst-gettext.m4 new file mode 100644 index 0000000..df817eb --- /dev/null +++ b/validate/common/m4/gst-gettext.m4 @@ -0,0 +1,28 @@ +dnl gettext setup + +dnl AG_GST_GETTEXT([gettext-package]) +dnl defines GETTEXT_PACKAGE and LOCALEDIR + +AC_DEFUN([AG_GST_GETTEXT], +[ + if test "$USE_NLS" = "yes"; then + GETTEXT_PACKAGE=[$1] + else + GETTEXT_PACKAGE=[NULL] + fi + AC_SUBST(GETTEXT_PACKAGE) + AC_DEFINE_UNQUOTED([GETTEXT_PACKAGE], "$GETTEXT_PACKAGE", + [gettext package name]) + + dnl make sure po/Makevars is kept in sync with GETTEXT_PACKAGE + if test -e "${srcdir}/po/Makevars"; then + if ! grep -e "$1" "${srcdir}/po/Makevars"; then + AC_MSG_ERROR([DOMAIN in po/Makevars does not match GETTEXT_PACKAGE $1]) + fi + fi + + dnl define LOCALEDIR in config.h + AS_AC_EXPAND(LOCALEDIR, $datadir/locale) + AC_DEFINE_UNQUOTED([LOCALEDIR], "$LOCALEDIR", + [gettext locale dir]) +]) diff --git a/validate/common/m4/gst-glib2.m4 b/validate/common/m4/gst-glib2.m4 new file mode 100644 index 0000000..5b9cd8b --- /dev/null +++ b/validate/common/m4/gst-glib2.m4 @@ -0,0 +1,130 @@ +dnl check for a minimum version of GLib + +dnl AG_GST_GLIB_CHECK([minimum-version-required]) + +AC_DEFUN([AG_GST_GLIB_CHECK], +[ + AC_REQUIRE([AS_NANO]) + + dnl Minimum required version of GLib + GLIB_REQ=[$1] + if test "x$GLIB_REQ" = "x" + then + AC_MSG_ERROR([Please specify a required version for GLib 2.0]) + fi + AC_SUBST(GLIB_REQ) + + dnl Check for glib with everything + AG_GST_PKG_CHECK_MODULES(GLIB, + glib-2.0 >= $GLIB_REQ gobject-2.0 gmodule-no-export-2.0) + + if test "x$HAVE_GLIB" = "xno"; then + AC_MSG_ERROR([This package requires GLib >= $GLIB_REQ to compile.]) + fi + + dnl Add define to tell GLib that threading is always enabled within GStreamer + dnl code (optimisation, bypasses checks if the threading system is enabled + dnl when using threading primitives) + GLIB_EXTRA_CFLAGS="$GLIB_EXTRA_CFLAGS -DG_THREADS_MANDATORY" + + dnl Define G_DISABLE_DEPRECATED for development versions + if test "x`expr $PACKAGE_VERSION_MINOR % 2`" = "x1" -a "x`expr $PACKAGE_VERSION_MICRO '<' 90`" = "x1"; then + GLIB_EXTRA_CFLAGS="$GLIB_EXTRA_CFLAGS -DG_DISABLE_DEPRECATED" + fi + + AC_ARG_ENABLE(gobject-cast-checks, + AS_HELP_STRING([--enable-gobject-cast-checks[=@<:@no/auto/yes@:>@]], + [Enable GObject cast checks]),[enable_gobject_cast_checks=$enableval], + [enable_gobject_cast_checks=auto]) + + if test "x$enable_gobject_cast_checks" = "xauto"; then + dnl Turn on cast checks only for development versions + if test "x`expr $PACKAGE_VERSION_MINOR % 2`" = "x1" -a "x`expr $PACKAGE_VERSION_MICRO '<' 90`" = "x1"; then + enable_gobject_cast_checks=yes + else + enable_gobject_cast_checks=no + fi + fi + + if test "x$enable_gobject_cast_checks" = "xno"; then + GLIB_EXTRA_CFLAGS="$GLIB_EXTRA_CFLAGS -DG_DISABLE_CAST_CHECKS" + fi + + AC_ARG_ENABLE(glib-asserts, + AS_HELP_STRING([--enable-glib-asserts[=@<:@no/auto/yes@:>@]], + [Enable GLib assertion]),[enable_glib_assertions=$enableval], + [enable_glib_assertions=auto]) + + if test "x$enable_glib_assertions" = "xauto"; then + dnl Enable assertions only for development versions + if test "x`expr $PACKAGE_VERSION_MINOR % 2`" = "x1" -a "x`expr $PACKAGE_VERSION_MICRO '<' 90`" = "x1"; then + enable_glib_assertions=yes + else + enable_glib_assertions=no + fi + fi + + if test "x$enable_glib_assertions" = "xno"; then + GLIB_EXTRA_CFLAGS="$GLIB_EXTRA_CFLAGS -DG_DISABLE_ASSERT" + fi + + dnl Find location of glib utils. People may want to or have to override these, + dnl e.g. in a cross-compile situation where PATH is a bit messed up. We need + dnl for these tools to work on the host, so can't just use the one from the + dnl GLib installation that pkg-config picks up, as that might be for a + dnl different target architecture. + dnl + dnl glib-genmarshal: + AC_MSG_CHECKING(for glib-genmarshal) + if test "x$GLIB_GENMARSHAL" != "x"; then + AC_MSG_RESULT([$GLIB_GENMARSHAL (from environment)]) + else + GLIB_GENMARSHAL=`$PKG_CONFIG --variable=glib_genmarshal glib-2.0` + if $GLIB_GENMARSHAL --version 2>/dev/null >/dev/null; then + AC_MSG_RESULT([$GLIB_GENMARSHAL (from pkg-config path)]) + else + AC_PATH_PROG(GLIB_GENMARSHAL, [glib-genmarshal], [glib-genmarshal]) + AC_MSG_RESULT([$GLIB_GENMARSHAL]) + fi + fi + if ! $GLIB_GENMARSHAL --version 2>/dev/null >/dev/null; then + AC_MSG_WARN([$GLIB_GENMARSHAL does not seem to work!]) + fi + AC_SUBST(GLIB_GENMARSHAL) + + dnl glib-mkenums: + AC_MSG_CHECKING(for glib-mkenums) + if test "x$GLIB_MKENUMS" != "x"; then + AC_MSG_RESULT([$GLIB_MKENUMS (from environment)]) + else + dnl glib-mkenums is written in perl so should always work really + GLIB_MKENUMS=`$PKG_CONFIG --variable=glib_mkenums glib-2.0` + AC_MSG_RESULT([$GLIB_MKENUMS]) + fi + if ! $GLIB_MKENUMS --version 2>/dev/null >/dev/null; then + AC_MSG_WARN([$GLIB_MKENUMS does not seem to work!]) + fi + AC_SUBST(GLIB_MKENUMS) + + AC_SUBST(GLIB_EXTRA_CFLAGS) + + dnl Now check for GIO + PKG_CHECK_MODULES(GIO, gio-2.0 >= $GLIB_REQ) + if test "x$HAVE_GIO" = "xno"; then + AC_MSG_ERROR([This package requires GIO >= $GLIB_REQ to compile.]) + fi + + GIO_MODULE_DIR="`$PKG_CONFIG --variable=giomoduledir gio-2.0`" + AC_DEFINE_UNQUOTED(GIO_MODULE_DIR, "$GIO_MODULE_DIR", + [The GIO modules directory.]) + GIO_LIBDIR="`$PKG_CONFIG --variable=libdir gio-2.0`" + AC_DEFINE_UNQUOTED(GIO_LIBDIR, "$GIO_LIBDIR", + [The GIO library directory.]) + GIO_PREFIX="`$PKG_CONFIG --variable=prefix gio-2.0`" + AC_DEFINE_UNQUOTED(GIO_PREFIX, "$GIO_PREFIX", + [The GIO install prefix.]) + + AC_SUBST(GIO_CFLAGS) + AC_SUBST(GIO_LIBS) + AC_SUBST(GIO_LDFLAGS) +]) diff --git a/validate/common/m4/gst-libxml2.m4 b/validate/common/m4/gst-libxml2.m4 new file mode 100644 index 0000000..4a843f0 --- /dev/null +++ b/validate/common/m4/gst-libxml2.m4 @@ -0,0 +1,52 @@ +dnl call this macro with the minimum required version as an argument +dnl this macro sets and AC_SUBSTs XML_CFLAGS and XML_LIBS +dnl it also sets LIBXML_PKG, used for the pkg-config file + +AC_DEFUN([AG_GST_LIBXML2_CHECK], +[ + dnl Minimum required version of libxml2 + dnl default to 2.4.9 if not specified + LIBXML2_REQ=ifelse([$1],,2.4.9,[$1]) + AC_SUBST(LIBXML2_REQ) + + dnl check for libxml2 + PKG_CHECK_MODULES(XML, libxml-2.0 >= $LIBXML2_REQ, + HAVE_LIBXML2=yes, [ + AC_MSG_RESULT(no) + HAVE_LIBXML2=no + ]) + if test "x$HAVE_LIBXML2" = "xyes"; then + AC_DEFINE(HAVE_LIBXML2, 1, [Define if libxml2 is available]) + else + AC_MSG_ERROR([ + Need libxml2 and development headers/files to build GStreamer. + + You can do without libxml2 if you pass --disable-loadsave to + configure, but that breaks ABI, so don't do that unless you + are building for an embedded setup and know what you are doing. + ]) + fi + dnl this is for the .pc file + LIBXML_PKG=', libxml-2.0' + AC_SUBST(LIBXML_PKG) + AC_SUBST(XML_LIBS) + AC_SUBST(XML_CFLAGS) + + dnl XML_LIBS might pull in -lz without zlib actually being on the system, so + dnl try linking with these LIBS and CFLAGS + ac_save_CFLAGS=$CFLAGS + ac_save_LIBS=$LIBS + CFLAGS="$CFLAGS $XML_CFLAGS" + LIBS="$LIBS $XML_LIBS" + AC_TRY_LINK([ +#include +#include +],[ +/* function body */ +], + AC_MSG_NOTICE([Test xml2 program linked]), + AC_MSG_ERROR([Could not link libxml2 test program. Check if you have the necessary dependencies.]) + ) + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" +]) diff --git a/validate/common/m4/gst-package-release-datetime.m4 b/validate/common/m4/gst-package-release-datetime.m4 new file mode 100644 index 0000000..bc885e3 --- /dev/null +++ b/validate/common/m4/gst-package-release-datetime.m4 @@ -0,0 +1,89 @@ +dnl macros to set GST_PACKAGE_RELEASE_DATETIME + +dnl =========================================================================== +dnl AG_GST_SET_PACKAGE_RELEASE_DATETIME +dnl +dnl Usage: +dnl +dnl AG_GST_SET_PACKAGE_RELEASE_DATETIME() +dnl AG_GST_SET_PACKAGE_RELEASE_DATETIME([no]...) +dnl sets the release datetime to the current date +dnl (no = this is not a release, but git or prerelease) +dnl +dnl AG_GST_SET_PACKAGE_RELEASE_DATETIME([YYYY-MM-DD]) +dnl AG_GST_SET_PACKAGE_RELEASE_DATETIME([yes], [YYYY-MM-DD]) +dnl sets the release datetime to the specified date (and time, if given) +dnl (yes = this is a release, not git or prerelease) +dnl +dnl AG_GST_SET_PACKAGE_RELEASE_DATETIME([yes], [DOAP-FILE], [RELEASE-VERSION]) +dnl sets the release date to the release date associated with version +dnl RELEASE-VERSION in the .doap file DOAP-FILE +dnl (yes = this is a release, not git or prerelease) +dnl +dnl We need to treat pre-releases like git because there won't be an entry +dnl in the .doap file for pre-releases yet, and we don't want to use the +dnl date of the last release either. +dnl =========================================================================== +AC_DEFUN([AG_GST_SET_PACKAGE_RELEASE_DATETIME], +[ + dnl AG_GST_SET_PACKAGE_RELEASE_DATETIME() + dnl AG_GST_SET_PACKAGE_RELEASE_DATETIME([yes]...) + if test "x$1" = "xno" -o "x$1" = "x"; then + GST_PACKAGE_RELEASE_DATETIME=`date -u "+%Y-%m-%dT%H:%MZ"` + elif test "x$1" = "xyes"; then + dnl AG_GST_SET_PACKAGE_RELEASE_DATETIME([no], ["YYYY-MM-DD"]) + dnl AG_GST_SET_PACKAGE_RELEASE_DATETIME([no], [DOAP-FILE], [RELEASE-VERSION]) + if ( echo $1 | grep '^20[1-9][0-9]-[0-1][0-9]-[0-3][0-9]' >/dev/null ) ; then + GST_PACKAGE_RELEASE_DATETIME=$1 + else + dnl we assume the .doap file contains the date as YYYY-MM-DD + YYYY_MM_DD=`sh "${srcdir}/common/extract-release-date-from-doap-file" $3 $2`; + if test "x$YYYY_MM_DD" != "x"; then + GST_PACKAGE_RELEASE_DATETIME=$YYYY_MM_DD + else + AC_MSG_ERROR([SET_PACKAGE_RELEASE_DATETIME: could not extract + release date for release version $3 from $2]) + GST_PACKAGE_RELEASE_DATETIME="" + fi + fi + dnl AG_GST_SET_PACKAGE_RELEASE_DATETIME([YYYY-MM-DD]) + elif ( echo $1 | grep '^20[1-9][0-9]-[0-1][0-9]-[0-3][0-9]' >/dev/null ) ; then + GST_PACKAGE_RELEASE_DATETIME=$1 + else + AC_MSG_WARN([SET_PACKAGE_RELEASE_DATETIME: invalid first argument]) + GST_PACKAGE_RELEASE_DATETIME="" + fi + + if test "x$GST_PACKAGE_RELEASE_DATETIME" = "x"; then + AC_MSG_WARN([Invalid package release date time: $GST_PACKAGE_RELEASE_DATETIME]) + else + AC_MSG_NOTICE([Setting GST_PACKAGE_RELEASE_DATETIME to $GST_PACKAGE_RELEASE_DATETIME]) + + AC_DEFINE_UNQUOTED([GST_PACKAGE_RELEASE_DATETIME], + ["$GST_PACKAGE_RELEASE_DATETIME"], + [GStreamer package release date/time for plugins as YYYY-MM-DD]) + fi +]) + +dnl =========================================================================== +dnl AG_GST_SET_PACKAGE_RELEASE_DATETIME_WITH_NANO +dnl +dnl Usage: +dnl +dnl AG_GST_SET_PACKAGE_RELEASE_DATETIME([NANO-VERSION], [DOAP-FILE], [RELEASE-VERSION]) +dnl if NANO-VERSION is 0, sets the release date to the release date associated +dnl with version RELEASE-VERSION in the .doap file DOAP-FILE, otherwise sets +dnl the release date and time to the current date/time. +dnl +dnl We need to treat pre-releases like git because there won't be an entry +dnl in the .doap file for pre-releases yet, and we don't want to use the +dnl date of the last release either. +dnl =========================================================================== +AC_DEFUN([AG_GST_SET_PACKAGE_RELEASE_DATETIME_WITH_NANO], +[ + if test "x$1" = "x0"; then + AG_GST_SET_PACKAGE_RELEASE_DATETIME([yes], [ $2 ], [ $3 ]) + else + AG_GST_SET_PACKAGE_RELEASE_DATETIME([no]) + fi +]) diff --git a/validate/common/m4/gst-parser.m4 b/validate/common/m4/gst-parser.m4 new file mode 100644 index 0000000..382ef72 --- /dev/null +++ b/validate/common/m4/gst-parser.m4 @@ -0,0 +1,55 @@ +AC_DEFUN([AG_GST_BISON_CHECK], +[ + dnl FIXME: check if AC_PROG_YACC is suitable here + dnl FIXME: make precious + AC_PATH_PROG(BISON_PATH, bison, no) + if test x$BISON_PATH = xno; then + AC_MSG_ERROR(Could not find bison) + fi + + dnl check bison version + dnl we need version >= 2.4 for the '<>' support + dnl in the parser. + dnl First lines observed: 'bison (GNU Bison) 2.3' or 'GNU Bison version 1.28' + bison_min_version=2.4 + bison_version=`$BISON_PATH --version | head -n 1 | sed 's/^[[^0-9]]*//' | sed 's/[[^0-9]]*$//' | cut -d' ' -f1` + AC_MSG_CHECKING([bison version $bison_version >= $bison_min_version]) + + if perl -we "exit (('v$bison_version' ge 'v$bison_min_version') ? 0 : 1)"; then + AC_MSG_RESULT([yes]) + else + AC_MSG_ERROR([no]) + fi +]) + +AC_DEFUN([AG_GST_FLEX_CHECK], +[ + dnl we require flex for building the parser + AC_PATH_PROG(FLEX_PATH, flex, no) + if test x$FLEX_PATH = xno; then + AC_MSG_ERROR(Could not find flex) + fi + + dnl check flex version + dnl we need version >= 2.5.31 for the reentrancy support + dnl in the parser. + flex_min_version=2.5.31 + flex_version=`$FLEX_PATH --version | head -n 1 | awk '{print $2}'` + AC_MSG_CHECKING([flex version $flex_version >= $flex_min_version]) + if perl -w < \$min_version_major) || + ((\$flex_version_major == \$min_version_major) && + (\$flex_version_minor > \$min_version_minor)) || + ((\$flex_version_major == \$min_version_major) && + (\$flex_version_minor == \$min_version_minor) && + (\$flex_version_micro >= \$min_version_micro))) + ? 0 : 1); +EOF + then + AC_MSG_RESULT(yes) + else + AC_MSG_ERROR([no]) + fi +]) diff --git a/validate/common/m4/gst-platform.m4 b/validate/common/m4/gst-platform.m4 new file mode 100644 index 0000000..40d6faf --- /dev/null +++ b/validate/common/m4/gst-platform.m4 @@ -0,0 +1,67 @@ +dnl AG_GST_PLATFORM +dnl Check for platform specific features and define some variables +dnl +dnl GST_EXTRA_MODULE_SUFFIX: contains a platform specific +dnl extra module suffix additional to G_MODULE_SUFFIX +dnl +dnl HAVE_OSX: Defined if compiling for OS X +dnl +dnl GST_HAVE_UNSAFE_FORK: Defined if fork is unsafe (Windows) +dnl +dnl HAVE_WIN32: Defined if compiling on Win32 +dnl + +AC_DEFUN([AG_GST_PLATFORM], +[ + AC_REQUIRE([AC_CANONICAL_HOST]) + + case $host_os in + rhapsody*) + AC_DEFINE_UNQUOTED(GST_EXTRA_MODULE_SUFFIX, [".dylib"], [Extra platform specific plugin suffix]) + ;; + darwin*) + AC_DEFINE_UNQUOTED(GST_EXTRA_MODULE_SUFFIX, [".dylib"], [Extra platform specific plugin suffix]) + AC_DEFINE_UNQUOTED(HAVE_OSX, 1, [Defined if compiling for OSX]) + ;; + cygwin*) + AC_DEFINE_UNQUOTED(GST_HAVE_UNSAFE_FORK, 1, [Defined when registry scanning through fork is unsafe]) + ;; + mingw* | msvc* | mks*) + dnl HAVE_WIN32 currently means "disable POSIXisms". + AC_DEFINE_UNQUOTED(HAVE_WIN32, 1, [Defined if compiling for Windows]) + + dnl define __MSVCRT_VERSION__ version if not set already by the + dnl compiler (ie. mostly for mingw). This is needed for things like + dnl __stat64 to be available. If set by the compiler, ensure it's + dnl new enough - we need at least WinXP SP2. + AC_TRY_COMPILE([ ], [ return __MSVCRT_VERSION__; ], [ + AC_TRY_COMPILE([ ], [ + #if __MSVCRT_VERSION__ < 0x0601 + #error "MSVCRT too old" + #endif + ], [ + AC_MSG_NOTICE([MSVCRT version looks ok]) + ], [ + AC_MSG_ERROR([MSVCRT version too old, need at least WinXP SP2]) + ]) + ], [ + AC_MSG_NOTICE([Setting MSVCRT version to 0x0601]) + AC_DEFINE_UNQUOTED(__MSVCRT_VERSION__, 0x0601, [We need at least WinXP SP2 for __stat64]) + ]) + ;; + *) + ;; + esac +]) + +AC_DEFUN([AG_GST_LIBTOOL_PREPARE], +[ + dnl Persuade libtool to also link (-l) a 'pure' (DirectX) static lib, + dnl i.e. as opposed to only import lib with dll counterpart. + dnl Needs to be tweaked before libtool's checks. + case $host_os in + cygwin* | mingw*) + lt_cv_deplibs_check_method=pass_all + ;; + esac +]) \ No newline at end of file diff --git a/validate/common/m4/gst-plugin-docs.m4 b/validate/common/m4/gst-plugin-docs.m4 new file mode 100644 index 0000000..0e2ab6e --- /dev/null +++ b/validate/common/m4/gst-plugin-docs.m4 @@ -0,0 +1,25 @@ +dnl AG_GST_PLUGIN_DOCS([MINIMUM-GTK-DOC-VERSION]) +dnl +dnl checks for prerequisites for the common/mangle-tmpl.py script +dnl used when building the plugin documentation + +AC_DEFUN([AG_GST_PLUGIN_DOCS], +[ + AC_BEFORE([GTK_DOC_CHECK],[$0])dnl check for gtk-doc first + AC_REQUIRE([AM_PATH_PYTHON])dnl find python first + + build_plugin_docs=no + AC_MSG_CHECKING([whether to build plugin documentation]) + if test x$enable_gtk_doc = xyes; then + if test x$PYTHON != x; then + build_plugin_docs=yes + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no (python not found)]) + fi + else + AC_MSG_RESULT([no (gtk-doc disabled or not available)]) + fi + + AM_CONDITIONAL(ENABLE_PLUGIN_DOCS, test x$build_plugin_docs = xyes) +]) diff --git a/validate/common/m4/gst-plugindir.m4 b/validate/common/m4/gst-plugindir.m4 new file mode 100644 index 0000000..c9e1301 --- /dev/null +++ b/validate/common/m4/gst-plugindir.m4 @@ -0,0 +1,17 @@ +dnl AG_GST_SET_PLUGINDIR + +dnl AC_DEFINE PLUGINDIR to the full location where plug-ins will be installed +dnl AC_SUBST plugindir, to be used in Makefile.am's + +AC_DEFUN([AG_GST_SET_PLUGINDIR], +[ + dnl define location of plugin directory + AS_AC_EXPAND(PLUGINDIR, ${libdir}/gstreamer-$GST_API_VERSION) + AC_DEFINE_UNQUOTED(PLUGINDIR, "$PLUGINDIR", + [directory where plugins are located]) + AC_MSG_NOTICE([Using $PLUGINDIR as the plugin install location]) + + dnl plugin directory configure-time variable for use in Makefile.am + plugindir="\$(libdir)/gstreamer-$GST_API_VERSION" + AC_SUBST(plugindir) +]) diff --git a/validate/common/m4/gst-valgrind.m4 b/validate/common/m4/gst-valgrind.m4 new file mode 100644 index 0000000..5c0d608 --- /dev/null +++ b/validate/common/m4/gst-valgrind.m4 @@ -0,0 +1,35 @@ +AC_DEFUN([AG_GST_VALGRIND_CHECK], +[ + dnl valgrind inclusion + AC_ARG_ENABLE(valgrind, + AC_HELP_STRING([--disable-valgrind], [disable run-time valgrind detection]), + [ + case "${enableval}" in + yes) USE_VALGRIND="$USE_DEBUG" ;; + no) USE_VALGRIND=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-valgrind) ;; + esac], + [ + USE_VALGRIND="$USE_DEBUG" + ]) dnl Default value + + VALGRIND_REQ="3.0" + if test "x$USE_VALGRIND" = xyes; then + PKG_CHECK_MODULES(VALGRIND, valgrind >= $VALGRIND_REQ, + USE_VALGRIND="yes", + [ + USE_VALGRIND="no" + AC_MSG_RESULT([no]) + ]) + fi + + if test "x$USE_VALGRIND" = xyes; then + AC_DEFINE(HAVE_VALGRIND, 1, [Define if valgrind should be used]) + AC_MSG_NOTICE(Using extra code paths for valgrind) + fi + AC_SUBST(VALGRIND_CFLAGS) + AC_SUBST(VALGRIND_LIBS) + + AC_PATH_PROG(VALGRIND_PATH, valgrind, no) + AM_CONDITIONAL(HAVE_VALGRIND, test ! "x$VALGRIND_PATH" = "xno") +]) diff --git a/validate/common/m4/gst-x11.m4 b/validate/common/m4/gst-x11.m4 new file mode 100644 index 0000000..c41ddff --- /dev/null +++ b/validate/common/m4/gst-x11.m4 @@ -0,0 +1,74 @@ +dnl macros for X-related detections +dnl AC_SUBST's HAVE_X, X_CFLAGS, X_LIBS +AC_DEFUN([AG_GST_CHECK_X], +[ + AC_PATH_XTRA + ac_cflags_save="$CFLAGS" + ac_cppflags_save="$CPPFLAGS" + CFLAGS="$CFLAGS $X_CFLAGS" + CPPFLAGS="$CPPFLAGS $X_CFLAGS" + + dnl now try to find the HEADER + HAVE_X="no" + AC_CHECK_HEADER([X11/Xlib.h], [ + dnl and then the library with the most uniquitous function + AC_CHECK_LIB(X11, [XSync], [HAVE_X="yes"], [], [$X_LIBS $X_PRE_LIBS $X_EXTRA_LIBS]) + ], [], [AC_INCLUDES_DEFAULT]) + + if test "x$HAVE_X" = "xno" + then + AC_MSG_NOTICE([cannot find X11 development files]) + else + dnl this is much more than we want + X_LIBS="$X_LIBS $X_PRE_LIBS $X_EXTRA_LIBS" + dnl AC_PATH_XTRA only defines the path needed to find the X libs, + dnl it does not add the libs; therefore we add them here + X_LIBS="$X_LIBS -lX11" + AC_SUBST(X_CFLAGS) + AC_SUBST(X_LIBS) + fi + AC_SUBST(HAVE_X) + + CFLAGS="$ac_cflags_save" + CPPFLAGS="$ac_cppflags_save" +]) + +dnl *** XVideo *** +dnl Look for the PIC library first, Debian requires it. +dnl Check debian-devel archives for gory details. +dnl 20020110: +dnl At the moment XFree86 doesn't distribute shared libXv due +dnl to unstable API. On many platforms you CAN NOT link a shared +dnl lib to a static non-PIC lib. This is what the xvideo GStreamer +dnl plug-in wants to do. So Debian distributes a PIC compiled +dnl version of the static lib for plug-ins to link to when it is +dnl inappropriate to link the main application to libXv directly. +dnl FIXME: add check if this platform can support linking to a +dnl non-PIC libXv, if not then don not use Xv. +dnl FIXME: perhaps warn user if they have a shared libXv since +dnl this is an error until XFree86 starts shipping one +AC_DEFUN([AG_GST_CHECK_XV], +[ + if test x$HAVE_X = xyes; then + AC_CHECK_LIB(Xv_pic, XvQueryExtension, + HAVE_XVIDEO="yes", HAVE_XVIDEO="no", + $X_LIBS -lXext) + + if test x$HAVE_XVIDEO = xyes; then + XVIDEO_LIBS="-lXv_pic -lXext" + AC_SUBST(XVIDEO_LIBS) + else + dnl try again using something else if we didn't find it first + if test x$HAVE_XVIDEO = xno; then + AC_CHECK_LIB(Xv, XvQueryExtension, + HAVE_XVIDEO="yes", HAVE_XVIDEO="no", + $X_LIBS -lXext) + + if test x$HAVE_XVIDEO = xyes; then + XVIDEO_LIBS="-lXv -lXext" + AC_SUBST(XVIDEO_LIBS) + fi + fi + fi + fi +]) diff --git a/validate/common/m4/gst.m4 b/validate/common/m4/gst.m4 new file mode 100644 index 0000000..d4c53cb --- /dev/null +++ b/validate/common/m4/gst.m4 @@ -0,0 +1,36 @@ +dnl AG_GST_INIT +dnl sets up use of GStreamer configure.ac macros +dnl all GStreamer autoconf macros are prefixed +dnl with AG_GST_ for public macros +dnl with _AG_GST_ for private macros +dnl +dnl We call AC_CANONICAL_TARGET and AC_CANONICAL_HOST so that +dnl it is valid before AC_ARG_PROGRAM is called + +AC_DEFUN([AG_GST_INIT], +[ + m4_pattern_forbid(^_?AG_GST_) + AC_REQUIRE([AC_CANONICAL_HOST]) dnl we use host_ variables + AC_REQUIRE([AC_CANONICAL_TARGET]) dnl we use target_ variables +]) + +dnl AG_GST_PKG_CONFIG_PATH +dnl +dnl sets up a GST_PKG_CONFIG_PATH variable for use in Makefile.am +dnl which contains the path of the in-tree pkgconfig directory first +dnl and then any paths specified in PKG_CONFIG_PATH. +dnl +dnl We do this mostly so we don't have to use unportable shell constructs +dnl such as ${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH} in Makefile.am to handle +dnl the case where the environment variable is not set, but also in order +dnl to avoid a trailing ':' in the PKG_CONFIG_PATH which apparently causes +dnl problems with pkg-config on windows with msys/mingw. +AC_DEFUN([AG_GST_PKG_CONFIG_PATH], +[ + GST_PKG_CONFIG_PATH="\$(top_builddir)/pkgconfig" + if test "x$PKG_CONFIG_PATH" != "x"; then + GST_PKG_CONFIG_PATH="$GST_PKG_CONFIG_PATH:$PKG_CONFIG_PATH" + fi + AC_SUBST([GST_PKG_CONFIG_PATH]) + AC_MSG_NOTICE([Using GST_PKG_CONFIG_PATH = $GST_PKG_CONFIG_PATH]) +]) diff --git a/validate/common/m4/gtk-doc.m4 b/validate/common/m4/gtk-doc.m4 new file mode 100644 index 0000000..b243f1c --- /dev/null +++ b/validate/common/m4/gtk-doc.m4 @@ -0,0 +1,70 @@ +dnl -*- mode: autoconf -*- + +# serial 1 + +dnl Usage: +dnl GTK_DOC_CHECK([minimum-gtk-doc-version]) +AC_DEFUN([GTK_DOC_CHECK], +[ + AC_REQUIRE([PKG_PROG_PKG_CONFIG]) + AC_BEFORE([AC_PROG_LIBTOOL],[$0])dnl setup libtool first + AC_BEFORE([AM_PROG_LIBTOOL],[$0])dnl setup libtool first + + dnl check for tools we added during development + AC_PATH_PROG([GTKDOC_CHECK],[gtkdoc-check]) + AC_PATH_PROGS([GTKDOC_REBASE],[gtkdoc-rebase],[true]) + AC_PATH_PROG([GTKDOC_MKPDF],[gtkdoc-mkpdf]) + + dnl for overriding the documentation installation directory + AC_ARG_WITH([html-dir], + AS_HELP_STRING([--with-html-dir=PATH], [path to installed docs]),, + [with_html_dir='${datadir}/gtk-doc/html']) + HTML_DIR="$with_html_dir" + AC_SUBST([HTML_DIR]) + + dnl enable/disable documentation building + AC_ARG_ENABLE([gtk-doc], + AS_HELP_STRING([--enable-gtk-doc], + [use gtk-doc to build documentation [[default=no]]]),, + [enable_gtk_doc=no]) + + if test x$enable_gtk_doc = xyes; then + ifelse([$1],[], + [PKG_CHECK_EXISTS([gtk-doc],, + AC_MSG_ERROR([gtk-doc not installed and --enable-gtk-doc requested]))], + [PKG_CHECK_EXISTS([gtk-doc >= $1],, + AC_MSG_ERROR([You need to have gtk-doc >= $1 installed to build $PACKAGE_NAME]))]) + dnl don't check for glib if we build glib + if test "x$PACKAGE_NAME" != "xglib"; then + dnl don't fail if someone does not have glib + PKG_CHECK_MODULES(GTKDOC_DEPS, glib-2.0 >= 2.10.0 gobject-2.0 >= 2.10.0,,) + fi + dnl don't rely on sed being pulled in implicitly. Fixes Solaris build. + if test -z "$SED"; then + AC_PROG_SED + fi + fi + + AC_MSG_CHECKING([whether to build gtk-doc documentation]) + AC_MSG_RESULT($enable_gtk_doc) + + dnl enable/disable output formats + AC_ARG_ENABLE([gtk-doc-html], + AS_HELP_STRING([--enable-gtk-doc-html], + [build documentation in html format [[default=yes]]]),, + [enable_gtk_doc_html=yes]) + AC_ARG_ENABLE([gtk-doc-pdf], + AS_HELP_STRING([--enable-gtk-doc-pdf], + [build documentation in pdf format [[default=no]]]),, + [enable_gtk_doc_pdf=no]) + + if test -z "$GTKDOC_MKPDF"; then + enable_gtk_doc_pdf=no + fi + + + AM_CONDITIONAL([ENABLE_GTK_DOC], [test x$enable_gtk_doc = xyes]) + AM_CONDITIONAL([GTK_DOC_BUILD_HTML], [test x$enable_gtk_doc_html = xyes]) + AM_CONDITIONAL([GTK_DOC_BUILD_PDF], [test x$enable_gtk_doc_pdf = xyes]) + AM_CONDITIONAL([GTK_DOC_USE_LIBTOOL], [test -n "$LIBTOOL"]) +]) diff --git a/validate/common/m4/introspection.m4 b/validate/common/m4/introspection.m4 new file mode 100644 index 0000000..589721c --- /dev/null +++ b/validate/common/m4/introspection.m4 @@ -0,0 +1,94 @@ +dnl -*- mode: autoconf -*- +dnl Copyright 2009 Johan Dahlin +dnl +dnl This file is free software; the author(s) gives unlimited +dnl permission to copy and/or distribute it, with or without +dnl modifications, as long as this notice is preserved. +dnl + +# serial 1 + +m4_define([_GOBJECT_INTROSPECTION_CHECK_INTERNAL], +[ + AC_BEFORE([AC_PROG_LIBTOOL],[$0])dnl setup libtool first + AC_BEFORE([AM_PROG_LIBTOOL],[$0])dnl setup libtool first + AC_BEFORE([LT_INIT],[$0])dnl setup libtool first + + dnl enable/disable introspection + m4_if([$2], [require], + [dnl + enable_introspection=yes + ],[dnl + AC_ARG_ENABLE(introspection, + AS_HELP_STRING([--enable-introspection[=@<:@no/auto/yes@:>@]], + [Enable introspection for this build]),, + [enable_introspection=auto]) + ])dnl + + AC_MSG_CHECKING([for gobject-introspection]) + + dnl presence/version checking + AS_CASE([$enable_introspection], + [no], [dnl + found_introspection="no (disabled, use --enable-introspection to enable)" + ],dnl + [yes],[dnl + PKG_CHECK_EXISTS([gobject-introspection-1.0],, + AC_MSG_ERROR([gobject-introspection-1.0 is not installed])) + PKG_CHECK_EXISTS([gobject-introspection-1.0 >= $1], + found_introspection=yes, + AC_MSG_ERROR([You need to have gobject-introspection >= $1 installed to build AC_PACKAGE_NAME])) + ],dnl + [auto],[dnl + PKG_CHECK_EXISTS([gobject-introspection-1.0 >= $1], found_introspection=yes, found_introspection=no) + ],dnl + [dnl + AC_MSG_ERROR([invalid argument passed to --enable-introspection, should be one of @<:@no/auto/yes@:>@]) + ])dnl + + AC_MSG_RESULT([$found_introspection]) + + INTROSPECTION_SCANNER= + INTROSPECTION_COMPILER= + INTROSPECTION_GENERATE= + INTROSPECTION_GIRDIR= + INTROSPECTION_TYPELIBDIR= + if test "x$found_introspection" = "xyes"; then + INTROSPECTION_SCANNER=`$PKG_CONFIG --variable=g_ir_scanner gobject-introspection-1.0` + INTROSPECTION_COMPILER=`$PKG_CONFIG --variable=g_ir_compiler gobject-introspection-1.0` + INTROSPECTION_GENERATE=`$PKG_CONFIG --variable=g_ir_generate gobject-introspection-1.0` + INTROSPECTION_GIRDIR=`$PKG_CONFIG --variable=girdir gobject-introspection-1.0` + INTROSPECTION_TYPELIBDIR="$($PKG_CONFIG --variable=typelibdir gobject-introspection-1.0)" + INTROSPECTION_CFLAGS=`$PKG_CONFIG --cflags gobject-introspection-1.0` + INTROSPECTION_LIBS=`$PKG_CONFIG --libs gobject-introspection-1.0` + INTROSPECTION_MAKEFILE=`$PKG_CONFIG --variable=datadir gobject-introspection-1.0`/gobject-introspection-1.0/Makefile.introspection + fi + AC_SUBST(INTROSPECTION_SCANNER) + AC_SUBST(INTROSPECTION_COMPILER) + AC_SUBST(INTROSPECTION_GENERATE) + AC_SUBST(INTROSPECTION_GIRDIR) + AC_SUBST(INTROSPECTION_TYPELIBDIR) + AC_SUBST(INTROSPECTION_CFLAGS) + AC_SUBST(INTROSPECTION_LIBS) + AC_SUBST(INTROSPECTION_MAKEFILE) + + AM_CONDITIONAL(HAVE_INTROSPECTION, test "x$found_introspection" = "xyes") +]) + + +dnl Usage: +dnl GOBJECT_INTROSPECTION_CHECK([minimum-g-i-version]) + +AC_DEFUN([GOBJECT_INTROSPECTION_CHECK], +[ + _GOBJECT_INTROSPECTION_CHECK_INTERNAL([$1]) +]) + +dnl Usage: +dnl GOBJECT_INTROSPECTION_REQUIRE([minimum-g-i-version]) + + +AC_DEFUN([GOBJECT_INTROSPECTION_REQUIRE], +[ + _GOBJECT_INTROSPECTION_CHECK_INTERNAL([$1], [require]) +]) diff --git a/validate/common/m4/orc.m4 b/validate/common/m4/orc.m4 new file mode 100644 index 0000000..26b2459 --- /dev/null +++ b/validate/common/m4/orc.m4 @@ -0,0 +1,70 @@ +dnl pkg-config-based checks for Orc + +dnl specific: +dnl ORC_CHECK([REQUIRED_VERSION]) + +AC_DEFUN([ORC_CHECK], +[ + ORC_REQ=ifelse([$1], , "0.4.6", [$1]) + + AC_ARG_ENABLE(orc, + AC_HELP_STRING([--enable-orc],[use Orc if installed]), + [case "${enableval}" in + auto) enable_orc=auto ;; + yes) enable_orc=yes ;; + no) enable_orc=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-orc) ;; + esac + ], + [enable_orc=auto]) dnl Default value + + if test "x$enable_orc" != "xno" ; then + PKG_CHECK_MODULES(ORC, orc-0.4 >= $ORC_REQ, [ + AC_DEFINE(HAVE_ORC, 1, [Use Orc]) + HAVE_ORC=yes + if test "x$ORCC" = "x" ; then + AC_MSG_CHECKING(for usable orcc) + ORCC=`$PKG_CONFIG --variable=orcc orc-0.4` + dnl check whether the orcc found by pkg-config can be run from the build environment + dnl if this is not the case (e.g. when cross-compiling) fall back to orcc from PATH + AS_IF([$ORCC --version 1> /dev/null 2> /dev/null], [], [ORCC=`which orcc`]) + AC_MSG_RESULT($ORCC) + fi + AC_SUBST(ORCC) + ORCC_FLAGS="--compat $ORC_REQ" + AC_SUBST(ORCC_FLAGS) + AS_IF([test "x$ORCC" = "x"], [HAVE_ORCC=no], [HAVE_ORCC=yes]) + ], [ + if test "x$enable_orc" = "xyes" ; then + AC_MSG_ERROR([--enable-orc specified, but Orc >= $ORC_REQ not found]) + fi + AC_DEFINE(DISABLE_ORC, 1, [Disable Orc]) + HAVE_ORC=no + HAVE_ORCC=no + ]) + else + AC_DEFINE(DISABLE_ORC, 1, [Disable Orc]) + HAVE_ORC=no + HAVE_ORCC=no + fi + AM_CONDITIONAL(HAVE_ORC, [test "x$HAVE_ORC" = "xyes"]) + AM_CONDITIONAL(HAVE_ORCC, [test "x$HAVE_ORCC" = "xyes"]) + +])) + +AC_DEFUN([ORC_OUTPUT], +[ + if test "$HAVE_ORC" = yes ; then + printf "configure: *** Orc acceleration enabled.\n" + else + if test "x$enable_orc" = "xno" ; then + printf "configure: *** Orc acceleration disabled by --disable-orc. Slower code paths\n" + printf " will be used.\n" + else + printf "configure: *** Orc acceleration disabled. Requires Orc >= $ORC_REQ, which was\n" + printf " not found. Slower code paths will be used.\n" + fi + fi + printf "\n" +]) + diff --git a/validate/common/m4/pkg.m4 b/validate/common/m4/pkg.m4 new file mode 100644 index 0000000..996e294 --- /dev/null +++ b/validate/common/m4/pkg.m4 @@ -0,0 +1,157 @@ +# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- +# +# Copyright © 2004 Scott James Remnant . +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program 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 +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# PKG_PROG_PKG_CONFIG([MIN-VERSION]) +# ---------------------------------- +AC_DEFUN([PKG_PROG_PKG_CONFIG], +[m4_pattern_forbid([^_?PKG_[A-Z_]+$]) +m4_pattern_allow([^PKG_CONFIG(_PATH)?$]) +AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])dnl +if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then + AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) +fi +if test -n "$PKG_CONFIG"; then + _pkg_min_version=m4_default([$1], [0.9.0]) + AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) + if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + PKG_CONFIG="" + fi + +fi[]dnl +])# PKG_PROG_PKG_CONFIG + +# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# +# Check to see whether a particular set of modules exists. Similar +# to PKG_CHECK_MODULES(), but does not set variables or print errors. +# +# +# Similar to PKG_CHECK_MODULES, make sure that the first instance of +# this or PKG_CHECK_MODULES is called, or make sure to call +# PKG_CHECK_EXISTS manually +# -------------------------------------------------------------- +AC_DEFUN([PKG_CHECK_EXISTS], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +if test -n "$PKG_CONFIG" && \ + AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then + m4_ifval([$2], [$2], [:]) +m4_ifvaln([$3], [else + $3])dnl +fi]) + + +# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) +# --------------------------------------------- +m4_define([_PKG_CONFIG], +[if test -n "$PKG_CONFIG"; then + if test -n "$$1"; then + pkg_cv_[]$1="$$1" + else + PKG_CHECK_EXISTS([$3], + [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`], + [pkg_failed=yes]) + fi +else + pkg_failed=untried +fi[]dnl +])# _PKG_CONFIG + +# _PKG_SHORT_ERRORS_SUPPORTED +# ----------------------------- +AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi[]dnl +])# _PKG_SHORT_ERRORS_SUPPORTED + + +# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], +# [ACTION-IF-NOT-FOUND]) +# +# +# Note that if there is a possibility the first call to +# PKG_CHECK_MODULES might not happen, you should be sure to include an +# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac +# +# +# -------------------------------------------------------------- +AC_DEFUN([PKG_CHECK_MODULES], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl +AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl + +pkg_failed=no +AC_MSG_CHECKING([for $1]) + +_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) +_PKG_CONFIG([$1][_LIBS], [libs], [$2]) + +m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS +and $1[]_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details.]) + +if test $pkg_failed = yes; then + _PKG_SHORT_ERRORS_SUPPORTED + if test $_pkg_short_errors_supported = yes; then + $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "$2"` + else + $1[]_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "$2"` + fi + # Put the nasty error message in config.log where it belongs + echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD + + ifelse([$4], , [AC_MSG_ERROR(dnl +[Package requirements ($2) were not met: + +$$1_PKG_ERRORS + +Consider adjusting the PKG_CONFIG_PATH environment variable if you +installed software in a non-standard prefix. + +_PKG_TEXT +])], + [AC_MSG_RESULT([no]) + $4]) +elif test $pkg_failed = untried; then + ifelse([$4], , [AC_MSG_FAILURE(dnl +[The pkg-config script could not be found or is too old. Make sure it +is in your PATH or set the PKG_CONFIG environment variable to the full +path to pkg-config. + +_PKG_TEXT + +To get pkg-config, see .])], + [$4]) +else + $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS + $1[]_LIBS=$pkg_cv_[]$1[]_LIBS + AC_MSG_RESULT([yes]) + ifelse([$3], , :, [$3]) +fi[]dnl +])# PKG_CHECK_MODULES diff --git a/validate/common/mangle-tmpl.py b/validate/common/mangle-tmpl.py new file mode 100644 index 0000000..51ea8c2 --- /dev/null +++ b/validate/common/mangle-tmpl.py @@ -0,0 +1,165 @@ +# -*- Mode: Python -*- +# vi:si:et:sw=4:sts=4:ts=4 + +""" +use the output from gst-xmlinspect.py to mangle tmpl/*.sgml and +insert/overwrite Short Description and Long Description +""" + +# FIXME: right now it uses pygst and scans on its own; +# we really should use inspect/*.xml instead since the result of +# gst-xmlinspect.py is committed by the docs maintainer, who can be +# expected to have pygst, but this step should be done for every docs build, +# so no pygst allowed + +# read in inspect/*.xml +# for every tmpl/element-(name).xml: mangle with details from element + +from __future__ import print_function, unicode_literals + +import glob +import re +import sys +import os + +class Tmpl: + def __init__(self, filename): + self.filename = filename + self._sectionids = [] + self._sections = {} + + def read(self): + """ + Read and parse the sections from the given file. + """ + lines = open(self.filename).readlines() + matcher = re.compile("\n") + id = None + + for line in lines: + match = matcher.search(line) + if match: + id = match.expand("\\1") + self._sectionids.append(id) + self._sections[id] = [] + else: + if not id: + sys.stderr.write( + "WARNING: line before a SECTION header: %s" % line) + else: + self._sections[id].append(line) + + def get_section(self, id): + """ + Get the content from the given section. + """ + return self._sections[id] + + def set_section(self, id, content): + """ + Replace the given section id with the given content. + """ + self._sections[id] = content + + def output(self): + """ + Return the output of the current template in the tmpl/*.sgml format. + """ + lines = [] + for id in self._sectionids: + lines.append("\n" % id) + for line in self._sections[id]: + lines.append(line) + + return "".join(lines) + + def write(self, backup=False): + """ + Write out the template file again, backing up the previous one. + """ + if backup: + target = self.filename + ".mangle.bak" + os.rename(self.filename, target) + + handle = open(self.filename, "w") + handle.write(self.output()) + handle.close() + +import xml.dom.minidom + +def get_elements(file): + elements = {} + doc = xml.dom.minidom.parse(file) + + elem = None + for e in doc.childNodes: + if e.nodeType == e.ELEMENT_NODE and e.localName == 'plugin': + elem = e + break + if elem == None: + return None + + elem2 = None + for e in elem.childNodes: + if e.nodeType == e.ELEMENT_NODE and e.localName == 'elements': + elem2 = e + break + if elem2 == None: + return None + + elem = elem2 + + for e in elem.childNodes: + if e.nodeType == e.ELEMENT_NODE and e.localName == 'element': + name = None + description = None + + for e2 in e.childNodes: + if e2.nodeType == e2.ELEMENT_NODE and e2.localName == 'name': + name = e2.childNodes[0].nodeValue.encode("UTF-8") + elif e2.nodeType == e2.ELEMENT_NODE and e2.localName == 'description': + if e2.childNodes: + description = e2.childNodes[0].nodeValue.encode("UTF-8") + else: + description = 'No description' + + if name != None and description != None: + elements[name] = {'description': description} + + return elements + +def main(): + if not len(sys.argv) == 3: + sys.stderr.write('Please specify the inspect/ dir and the tmpl/ dir') + sys.exit(1) + + inspectdir = sys.argv[1] + tmpldir = sys.argv[2] + + # parse all .xml files; build map of element name -> short desc + #for file in glob.glob("inspect/plugin-*.xml"): + elements = {} + for file in glob.glob("%s/plugin-*.xml" % inspectdir): + elements.update(get_elements(file)) + + for file in glob.glob("%s/element-*.sgml" % tmpldir): + base = os.path.basename(file) + element = base[len("element-"):-len(".sgml")] + tmpl = Tmpl(file) + tmpl.read() + if element in elements.keys(): + description = elements[element]['description'] + tmpl.set_section("Short_Description", "%s\n\n" % description) + + # put in an include if not yet there + line = '' + \ + '' + \ + '\n' + section = tmpl.get_section("Long_Description") + if not section[0] == line: + section.insert(0, line) + tmpl.set_section("Long_Description", section) + tmpl.write() + +main() diff --git a/validate/common/orc.mak b/validate/common/orc.mak new file mode 100644 index 0000000..a232b5d --- /dev/null +++ b/validate/common/orc.mak @@ -0,0 +1,76 @@ +# +# This is a makefile.am fragment to build Orc code. +# +# Define ORC_SOURCE and then include this file, such as: +# +# ORC_SOURCE=gstadderorc +# include $(top_srcdir)/common/orc.mak +# +# This fragment will create tmp-orc.c and gstadderorc.h from +# gstadderorc.orc. +# +# When 'make dist' is run at the top level, or 'make orc-update' +# in a directory including this fragment, the generated source +# files will be copied to $(ORC_SOURCE)-dist.[ch]. These files +# should be checked in to git, since they are used if Orc is +# disabled. +# +# Note that this file defines BUILT_SOURCES, so any later usage +# of BUILT_SOURCES in the Makefile.am that includes this file +# must use '+='. +# + + +EXTRA_DIST = $(ORC_SOURCE).orc + +ORC_NODIST_SOURCES = tmp-orc.c $(ORC_SOURCE).h +BUILT_SOURCES = tmp-orc.c $(ORC_SOURCE).h + + +orc-update: tmp-orc.c $(ORC_SOURCE).h + $(top_srcdir)/common/gst-indent tmp-orc.c + cp tmp-orc.c $(srcdir)/$(ORC_SOURCE)-dist.c + cp $(ORC_SOURCE).h $(srcdir)/$(ORC_SOURCE)-dist.h + +orcc_v_gen = $(orcc_v_gen_$(V)) +orcc_v_gen_ = $(orcc_v_gen_$(AM_DEFAULT_VERBOSITY)) +orcc_v_gen_0 = @echo " ORCC $@"; + +cp_v_gen = $(cp_v_gen_$(V)) +cp_v_gen_ = $(cp_v_gen_$(AM_DEFAULT_VERBOSITY)) +cp_v_gen_0 = @echo " CP $@"; + +if HAVE_ORCC +tmp-orc.c: $(srcdir)/$(ORC_SOURCE).orc + $(orcc_v_gen)$(ORCC) $(ORCC_FLAGS) --implementation --include glib.h -o tmp-orc.c $(srcdir)/$(ORC_SOURCE).orc + +$(ORC_SOURCE).h: $(srcdir)/$(ORC_SOURCE).orc + $(orcc_v_gen)$(ORCC) $(ORCC_FLAGS) --header --include glib.h -o $(ORC_SOURCE).h $(srcdir)/$(ORC_SOURCE).orc +else +tmp-orc.c: $(srcdir)/$(ORC_SOURCE).orc $(srcdir)/$(ORC_SOURCE)-dist.c + $(cp_v_gen)cp $(srcdir)/$(ORC_SOURCE)-dist.c tmp-orc.c + +$(ORC_SOURCE).h: $(srcdir)/$(ORC_SOURCE).orc $(srcdir)/$(ORC_SOURCE)-dist.c + $(cp_v_gen)cp $(srcdir)/$(ORC_SOURCE)-dist.h $(ORC_SOURCE).h +endif + +clean-local: clean-orc +.PHONY: clean-orc +clean-orc: + rm -f tmp-orc.c $(ORC_SOURCE).h + +dist-hook: dist-hook-orc +.PHONY: dist-hook-orc + +# we try and copy updated orc -dist files below, but don't fail if it +# doesn't work as the srcdir might not be writable +dist-hook-orc: tmp-orc.c $(ORC_SOURCE).h + $(top_srcdir)/common/gst-indent tmp-orc.c + rm -f tmp-orc.c~ + cmp -s tmp-orc.c $(srcdir)/$(ORC_SOURCE)-dist.c || \ + cp tmp-orc.c $(srcdir)/$(ORC_SOURCE)-dist.c || true + cmp -s $(ORC_SOURCE).h $(srcdir)/$(ORC_SOURCE)-dist.h || \ + cp $(ORC_SOURCE).h $(srcdir)/$(ORC_SOURCE)-dist.h || true + cp -p tmp-orc.c $(distdir)/$(ORC_SOURCE)-dist.c + cp -p $(ORC_SOURCE).h $(distdir)/$(ORC_SOURCE)-dist.h + diff --git a/validate/common/parallel-subdirs.mak b/validate/common/parallel-subdirs.mak new file mode 100644 index 0000000..36885df --- /dev/null +++ b/validate/common/parallel-subdirs.mak @@ -0,0 +1,13 @@ +# include this at the end of $MODULE/ext/Makefile.am to force make to +# build subdirectories in parallel when make -jN is used. We will end up +# descending into all subdirectories a second time, but only after the first +# (parallel) run has finished, so it should go right through the second time. + +.PHONY: independent-subdirs $(SUBDIRS) + +independent-subdirs: $(SUBDIRS) + +$(SUBDIRS): + $(MAKE) -C $@ + +all-recursive: independent-subdirs diff --git a/validate/common/plugins.xsl b/validate/common/plugins.xsl new file mode 100644 index 0000000..60515b6 --- /dev/null +++ b/validate/common/plugins.xsl @@ -0,0 +1,209 @@ + + + + + + + + + + + + -plugins- + + + + + + + + + + + + + + Element Information + + + + + plugin + + + + plugin- + + + + + + + + author + + + + + + + class + + + + + + + + + + Element Pads + + + + + name + + + + + + + direction + + + + + + + presence + + + + + + + + + details + + + + + + + + + + + + + + + + + + + + + -plugins-plugin- + + + + + + 3 + FIXME Library + + + + + + + + + + plugin- + + + + + + + Plugin Information + + + + filename + + + + + + + version + + + + + + + run-time license + + + + + + + package + + + + + + + origin + + + + + + + + + + + + + + + + + + + + + + + Elements + + + + + + + + + + + + + + diff --git a/validate/common/po.mak b/validate/common/po.mak new file mode 100644 index 0000000..e019fac --- /dev/null +++ b/validate/common/po.mak @@ -0,0 +1,4 @@ +# rule to download the latest .po files +download-po: $(top_srcdir)/common/download-translations + $(top_srcdir)/common/download-translations $(PACKAGE) + diff --git a/validate/common/release.mak b/validate/common/release.mak new file mode 100644 index 0000000..715657b --- /dev/null +++ b/validate/common/release.mak @@ -0,0 +1,34 @@ +# include this snippet to add a common release: target by using +# include $(top_srcdir)/common/release.mak + +release: dist + @$(MAKE) $(PACKAGE)-$(VERSION).tar.xz.sha256sum + @echo + @echo "=================================================================================================" + @echo "http://gstreamer.freedesktop.org/src/$(PACKAGE)/$(PACKAGE)-$(VERSION).tar.xz" + @cat $(PACKAGE)-$(VERSION).tar.xz.sha256sum + @echo "=================================================================================================" + @if [ -d ~/releases/ ]; then \ + cp -v $(PACKAGE)-$(VERSION).tar.xz ~/releases/; \ + fi + @if [ -d ../www/data/src ]; then \ + mv -v $(PACKAGE)-$(VERSION).tar.xz ../www/data/src/$(PACKAGE)/ ; \ + mv -v $(PACKAGE)-$(VERSION).tar.xz.sha256sum ../www/data/src/$(PACKAGE)/ ; \ + fi + @echo "=================================================================================================" + +# generate sha256 sum files +%.sha256sum: % + @sha256sum $< > $@ + +# check that no marshal or enumtypes files are included +# this in turn ensures that distcheck fails for missing .list files which is currently +# shadowed when the corresponding .c and .h files are included. +distcheck-hook: + @test "x" = "x`find $(distdir) -name \*-enumtypes.[ch] | grep -v win32`" && \ + test "x" = "x`find $(distdir) -name \*-marshal.[ch]`" || \ + ( echo "*** Leftover enumtypes or marshal files in the tarball." && \ + echo "*** Make sure the following files are not disted:" && \ + find $(distdir) -name \*-enumtypes.[ch] | grep -v win32 && \ + find $(distdir) -name \*-marshal.[ch] && \ + false ) diff --git a/validate/common/scangobj-merge.py b/validate/common/scangobj-merge.py new file mode 100755 index 0000000..46736b4 --- /dev/null +++ b/validate/common/scangobj-merge.py @@ -0,0 +1,314 @@ +#!/usr/bin/python +# -*- Mode: Python -*- +# vi:si:et:sw=4:sts=4:ts=4 + +""" +parse, merge and write gstdoc-scanobj files +""" + +from __future__ import print_function, unicode_literals + +import codecs +import os +import sys + +def debug(*args): + pass + +# OrderedDict class based on +# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/107747 +# Licensed under the Python License +class OrderedDict(dict): + def __init__(self): + self._keys = [] + dict.__init__(self) + + def __delitem__(self, key): + dict.__delitem__(self, key) + self._keys.remove(key) + + def __setitem__(self, key, item): + dict.__setitem__(self, key, item) + if key not in self._keys: self._keys.append(key) + + def clear(self): + dict.clear(self) + self._keys = [] + + def copy(self): + dict = dict.copy(self) + dict._keys = self._keys[:] + return dict + + def items(self): + return zip(self._keys, self.values()) + + def keys(self): + return self._keys + + def popitem(self): + try: + key = self._keys[-1] + except IndexError: + raise KeyError('dictionary is empty') + + val = self[key] + del self[key] + + return (key, val) + + def setdefault(self, key, failobj = None): + dict.setdefault(self, key, failobj) + if key not in self._keys: self._keys.append(key) + + def update(self, dict): + dict.update(self, dict) + for key in dict.keys(): + if key not in self._keys: self._keys.append(key) + + def values(self): + return map(self.get, self._keys) + +class Object: + def __init__(self, name): + self._signals = OrderedDict() + self._args = OrderedDict() + self.name = name + + def __repr__(self): + return "" % self.name + + def add_signal(self, signal, overwrite=True): + if not overwrite and signal.name in self._signals: + raise IndexError("signal %s already in %r" % (signal.name, self)) + self._signals[signal.name] = signal + + def add_arg(self, arg, overwrite=True): + if not overwrite and arg.name in self._args: + raise IndexError("arg %s already in %r" % (arg.name, self)) + self._args[arg.name] = arg + +class Docable: + def __init__(self, **kwargs): + for key in self.attrs: + setattr(self, key, kwargs[key]) + self.dict = kwargs + + def __repr__(self): + return "<%r %s>" % (str(self.__class__), self.name) + +class Signal(Docable): + attrs = ['name', 'returns', 'args'] + +class Arg(Docable): + attrs = ['name', 'type', 'range', 'flags', 'nick', 'blurb', 'default'] + +class GDoc: + def load_file(self, filename): + try: + lines = codecs.open(filename, encoding='utf-8').readlines() + self.load_data("".join(lines)) + except IOError: + print ("WARNING - could not read from %s" % filename) + except UnicodeDecodeError as e: + print ("WARNING - could not parse %s: %s" % (filename, e)) + + def save_file(self, filename, backup=False): + """ + Save the information to the given file if the file content changed. + """ + olddata = None + try: + lines = codecs.open(filename, encoding='utf-8').readlines() + olddata = "".join(lines) + except IOError: + print ("WARNING - could not read from %s" % filename) + newdata = self.get_data() + if olddata and olddata == newdata: + return + + if olddata: + if backup: + os.rename(filename, filename + '.bak') + + handle = codecs.open(filename, "w", encoding='utf-8') + handle.write(newdata) + handle.close() + +class Signals(GDoc): + def __init__(self): + self._objects = OrderedDict() + + def load_data(self, data): + """ + Load the .signals lines, creating our list of objects and signals. + """ + import re + smatcher = re.compile( + '(?s)' # make . match \n + '\n(.*?)\n' + ) + nmatcher = re.compile( + '' + '(?P\S*)' # store object + '::' + '(?P\S*)' # store signal + '' + ) + rmatcher = re.compile( + '(?s)' # make . match \n + '(?P\S*)\n' # store returns + '(?P.*)' # store args + ) + for block in smatcher.findall(data): + nmatch = nmatcher.search(block) + if nmatch: + o = nmatch.group('object') + debug("Found object", o) + debug("Found signal", nmatch.group('signal')) + if o not in self._objects: + object = Object(o) + self._objects[o] = object + + rmatch = rmatcher.search(block) + if rmatch: + dict = rmatch.groupdict().copy() + dict['name'] = nmatch.group('signal') + signal = Signal(**dict) + self._objects[o].add_signal(signal) + + def get_data(self): + lines = [] + for o in self._objects.values(): + for s in o._signals.values(): + block = """ +%(object)s::%(name)s +%(returns)s +%(args)s +""" + d = s.dict.copy() + d['object'] = o.name + lines.append(block % d) + + return "\n".join(lines) + '\n' + +class Args(GDoc): + def __init__(self): + self._objects = OrderedDict() + + def load_data(self, data): + """ + Load the .args lines, creating our list of objects and args. + """ + import re + amatcher = re.compile( + '(?s)' # make . match \n + '\n(.*?)\n' + ) + nmatcher = re.compile( + '' + '(?P\S*)' # store object + '::' + '(?P\S*)' # store arg + '' + ) + rmatcher = re.compile( + '(?s)' # make . match \n + '(?P\S*)\n' # store type + '(?P.*?)\n' # store range + '(?P\S*)\n' # store flags + '(?P.*?)\n' # store nick + '(?P.*?)\n' # store blurb + '(?P.*?)\n' # store default + ) + for block in amatcher.findall(data): + nmatch = nmatcher.search(block) + if nmatch: + o = nmatch.group('object') + debug("Found object", o) + debug("Found arg", nmatch.group('arg')) + if o not in self._objects: + object = Object(o) + self._objects[o] = object + + rmatch = rmatcher.search(block) + if rmatch: + dict = rmatch.groupdict().copy() + dict['name'] = nmatch.group('arg') + arg = Arg(**dict) + self._objects[o].add_arg(arg) + else: + print ("ERROR: could not match arg from block %s" % block) + + def get_data(self): + lines = [] + for o in self._objects.values(): + for a in o._args.values(): + block = """ +%(object)s::%(name)s +%(type)s +%(range)s +%(flags)s +%(nick)s +%(blurb)s +%(default)s + +""" + d = a.dict.copy() + d['object'] = o.name + lines.append(block % d) + + return "\n".join(lines) + '\n' + +class SingleLine(GDoc): + def __init__(self): + self._objects = [] + + def load_data(self, data): + """ + Load the .interfaces/.prerequisites lines, merge duplicates + """ + # split data on '\n' + lines = data.splitlines(); + # merge them into self._objects + for line in lines: + if line not in self._objects: + self._objects.append(line) + + def get_data(self): + lines = sorted(self._objects) + return "\n".join(lines) + '\n' + +def main(argv): + modulename = None + try: + modulename = argv[1] + except IndexError: + sys.stderr.write('Please provide a documentation module name\n') + sys.exit(1) + + signals = Signals() + signals.load_file(modulename + '.signals') + signals.load_file(modulename + '.signals.new') + signals.save_file(modulename + '.signals', backup=True) + os.unlink(modulename + '.signals.new') + + args = Args() + args.load_file(modulename + '.args') + args.load_file(modulename + '.args.new') + args.save_file(modulename + '.args', backup=True) + os.unlink(modulename + '.args.new') + + ifaces = SingleLine() + ifaces.load_file(modulename + '.interfaces') + ifaces.load_file(modulename + '.interfaces.new') + ifaces.save_file(modulename + '.interfaces', backup=True) + os.unlink(modulename + '.interfaces.new') + + prereq = SingleLine() + prereq.load_file(modulename + '.prerequisites') + prereq.load_file(modulename + '.prerequisites.new') + prereq.save_file(modulename + '.prerequisites', backup=True) + os.unlink(modulename + '.prerequisites.new') + +main(sys.argv) diff --git a/validate/common/update-autogen b/validate/common/update-autogen new file mode 100755 index 0000000..d267556 --- /dev/null +++ b/validate/common/update-autogen @@ -0,0 +1,48 @@ +#!/bin/bash + +if [ ! -f "common/update-autogen" ]; then + echo "Run ./common/update-autogen from the top-level source directory of a GStreamer module"; + exit 1; +fi + +if ! ls -1 *.doap 2>/dev/null >/dev/null; then + echo "Could not find *.doap file"; + exit 1; +fi + +PACKAGE=`ls -1 *.doap | head -n1 | sed -e 's/.doap$//'` + +#echo "Package: $PACKAGE" + +DIR=`mktemp -d` +if [[ $? != 0 ]]; then + echo "Could not create temp dir"; + exit 1; +fi + +TEMP_AUTOGEN_SH="$DIR/autogen.sh" +echo "\ +#!/bin/sh +# +# $PACKAGE autogen.sh +# +# Run this to generate all the initial makefiles, etc. +# +# This file has been generated from common/autogen.sh.in via common/update-autogen + +" > $TEMP_AUTOGEN_SH + +sed \ + -e "s/@API_VERSION@/1.0/g" \ + -e "s/@PACKAGE@/$PACKAGE/g" \ + -e "s/@SRCFILE@/$PACKAGE.doap/g" < common/autogen.sh.in >> $TEMP_AUTOGEN_SH + +chmod +x $TEMP_AUTOGEN_SH + +mv $TEMP_AUTOGEN_SH autogen.sh || { + echo "Failed to update autogen.sh" + exit 1; +} +rmdir $DIR + +echo "Updated $PACKAGE autogen.sh" diff --git a/validate/common/update-common b/validate/common/update-common new file mode 100755 index 0000000..3bb4549 --- /dev/null +++ b/validate/common/update-common @@ -0,0 +1,187 @@ +#!/bin/bash +# +# This script will update all the modules listed below so that +# common points to master in the common module. +# +# If you have many of the GStreamer modules checked out in a particular +# directory, it's best to run this script from that directory. For +# example, I check everything out in ~/gst, so this file is +# ~/gst/common/update-common. To do an update, I do +# 'cd ~/gst ; ./common/update-common'. This will automatically use +# the refs in your existing checkout when cloning the temporary +# checkout. Alternatively, you can use the reference variable below. +# +# Options: +# +# --dry-run : pass --dry-run to git push, don't actually push the changes +# --keep : keep temporary checkouts around instead of deleting them + +# Set this variable to point to any directory containing existing +# git # checkouts, and git will pull objects from there, decreasing +# network usage. +BRANCH=master +reference=~/gst +PUSHURL=ssh://git.freedesktop.org/git/gstreamer +DRY_RUN= +KEEP=no +COMMON_COMMIT= + +set -e +set -x + +modules="gstreamer gst-plugins-base gst-plugins-good gst-plugins-bad \ + gst-plugins-ugly gst-libav gst-omx \ + gst-rtsp-server gst-editing-services" + +topdir=`pwd` +dir=`mktemp -d $topdir/common-update-XXXXXX` + +# process command line arguments +set +x +for arg in $@ +do + case $arg in + --dry-run) + DRY_RUN="--dry-run"; + ;; + --keep) + KEEP="yes"; + ;; + --commit=*) + COMMON_COMMIT="${arg#*=}"; + DRY_RUN="--dry-run"; + KEEP="yes"; + ;; + --help) + echo + echo "update-common supported command line options:" + echo + echo " --dry-run Don't actually push changes to the repository, use git push --dry-run" + echo + echo " --keep Don't delete temporary git checkout used for update operation, keep it around" + echo + echo " --commit=REF Update common to commit reference REF (for local testing, implies --dry-run --keep)" + echo + exit 0; + ;; + *) + echo "Unknown command line argument $arg" + echo "Supported: --dry-run, --keep" + exit 1; + ;; + esac +done +set -x + +# create temporary checkouts of the modules +for module in $modules +do + cd $dir + if test -e $reference/$module/.git ; then + pushd $reference/$module + PUSHURL=`git config remote.origin.url | sed 's@\(git/gstreamer\).*@\1@'` + popd + git clone --reference $reference/$module/.git --shared ssh://git.freedesktop.org/git/gstreamer/$module + elif test -e $topdir/$module/.git ; then + pushd $topdir/$module + PUSHURL=`git config remote.origin.url | sed 's@\(git/gstreamer\).*@\1@'` + popd + git clone --reference $topdir/$module/.git --shared $PUSHURL/$module + else + git clone $PUSHURL/$module + fi + cd $dir/$module + + # ignore modules that don't have such a branch + if ! git show-ref origin/$BRANCH >/dev/null; then + continue; + fi + + if test $BRANCH = 'master'; then + git checkout $BRANCH + else + git checkout -b $BRANCH origin/$BRANCH + fi + + git submodule init + + # avoid downloading common submodule by re-using existing common checkout + if test -e $reference/common/.git ; then + git submodule update --reference $reference/common -- common + elif test -e $topdir/common/.git ; then + git submodule update --reference $topdir/common -- common + else + git submodule update + fi + + # avoid downloading libav submodule by re-using existing checkout + if test "$module" = "gst-libav"; then + if test -e $reference/gst-libav/gst-libs/ext/libav/.git ; then + git submodule update --reference $reference/gst-libav/gst-libs/ext/libav -- gst-libs/ext/gst-libav + elif test -e $topdir/gst-libav/gst-libs/ext/libav/.git ; then + git submodule update --reference $topdir/gst-libav/gst-libs/ext/libav/ -- gst-libs/ext/libav + else + git submodule update + fi + fi + + # for good measure in case there are any other submodules anywhere + git submodule update + + cd $dir/$module/common + ref_from=`git log --pretty=format:%h -n 1 HEAD` + if test $BRANCH = 'master'; then + git checkout $BRANCH + else + git checkout -b $BRANCH origin/$BRANCH + fi + git pull origin + if [ -n "$COMMON_COMMIT" ] ; then + echo "Forcing common to commit $COMMON_COMMIT"; + git reset --hard $COMMON_COMMIT || { + echo "Failed to git reset to $COMMON_COMMIT"; + exit 1; + } + fi + ref_to=`git log --pretty=format:%h -n 1 HEAD` + echo updating common from $ref_from to $ref_to + if [ "$ref_from" != "$ref_to" ] ; then + cd $dir/$module + # update autogen.sh for selected modules + case $module in + gstreamer|gst-plugins-base|gst-plugins-good|gst-plugins-ugly|gst-plugins-bad|gst-libav|gst-editing-services|gst-rtsp-server|gst-omx ) + ./common/update-autogen + git add autogen.sh + ;; + *) + ;; + esac + # update README and MAINTAINERS for selected modules + case $module in + gstreamer|gst-plugins-base|gst-plugins-good|gst-plugins-ugly|gst-plugins-bad ) + ./common/update-readmes --run-git-add + ;; + *) + ;; + esac + # and finally update the common submodule + git add common + git commit -m "Automatic update of common submodule + +From $ref_from to $ref_to" + fi + cd $dir +done + +for module in $modules +do + cd $dir/$module + if git show-ref origin/$BRANCH >/dev/null; then + git push $DRY_RUN origin $BRANCH + fi +done + +# delete temporary checkouts again +if test "x$KEEP" != "xyes"; then + rm -rf $dir +fi diff --git a/validate/common/update-readmes b/validate/common/update-readmes new file mode 100755 index 0000000..1eb9912 --- /dev/null +++ b/validate/common/update-readmes @@ -0,0 +1,42 @@ +#!/bin/bash +# +# update-readmes +# +# Updates a module's README and MAINTAINERS files from the copy in the +# common submodule. + +README_FILES="README README.static-linking MAINTAINERS" + +if [ ! -f "common/update-readmes" -o ! -f configure.ac ]; then + echo "Run ./common/update-readmes from the top-level source directory of a GStreamer module"; + exit 1; +fi + +MAJOR_VERSION=`grep '^AC_INIT' configure.ac | sed -e 's/[^0-9]*\([0-9]\)\.\([0-9]*\).*/\1/'` +MINOR_VERSION=`grep '^AC_INIT' configure.ac | sed -e 's/[^0-9]*\([0-9]\)\.\([0-9]*\).*/\2/'` + +if test x$MAJOR_VERSION = x -o x$MINOR_VERSION = x ; then + echo "Failed to extract major/minor version"; + exit 1; +fi + +let m=$MINOR_VERSION%2 +if test $m = 0; then + SERIES_VERSION="$MAJOR_VERSION.$MINOR_VERSION.x stable series" +else + SERIES_VERSION="$MAJOR_VERSION.$MINOR_VERSION.x development series" +fi +#echo "$SERIES_VERSION" + +for f in $README_FILES ; do + cp --preserve "common/$f" $f || { + echo "Failed to update $f" + exit 1; + } +done + +sed -i "s/@SERIES_VERSION@/$SERIES_VERSION/g" README + +if test x$1 = "x--run-git-add"; then + git add $README_FILES; +fi diff --git a/validate/common/upload-doc.mak b/validate/common/upload-doc.mak new file mode 100644 index 0000000..c1c4945 --- /dev/null +++ b/validate/common/upload-doc.mak @@ -0,0 +1,64 @@ +# this snippet is to be included by both our docbook manuals +# and gtk-doc API references + +# it adds an upload target to each of these dir's Makefiles + +# each Makefile.am should define the following variables: +# - DOC: the base name of the documentation +# (faq, manual, pwg, gstreamer, gstreamer-libs) +# - FORMATS: the formats in which DOC is output +# (html ps pdf) + +# if you want to use it, make sure your $HOME/.ssh/config file contains the +# correct User entry for the Host entry for the DOC_SERVER + +# these variables define the location of the online docs +DOC_SERVER = gstreamer.freedesktop.org +DOC_BASE = /srv/gstreamer.freedesktop.org/www/data/doc +DOC_URL = $(DOC_SERVER):$(DOC_BASE) + +upload: $(FORMATS) + @if echo $(FORMATS) | grep html > /dev/null; then \ + echo "Preparing docs for upload (rebasing cross-references) ..." ; \ + if test x$(builddir) != x$(srcdir); then \ + echo "make upload can only be used if srcdir == builddir"; \ + exit 1; \ + fi; \ + # gtkdoc-rebase sometimes gets confused, so reset everything to \ + # local links before rebasing to online links \ + gtkdoc-rebase --html-dir=$(builddir)/html 2>/dev/null 2>/dev/null ; \ + rebase=`gtkdoc-rebase --verbose --online --html-dir=$(builddir)/html` ; \ + echo "$$rebase" | grep -e "On-*line"; \ + for req in glib gobject gstreamer gstreamer-libs gst-plugins-base-libs; do \ + if ! ( echo "$$rebase" | grep -i -e "On-*line.*/$$req/" ); then \ + echo "===============================================================================" ; \ + echo " Could not determine online location for $$req docs. Cross-referencing will be " ; \ + echo " broken, so not uploading. Make sure the library's gtk-doc documentation is " ; \ + echo " installed somewhere in /usr/share/gtk-doc. " ; \ + echo "===============================================================================" ; \ + exit 1; \ + fi; \ + done; \ + export SRC="$$SRC html"; \ + fi; \ + if echo $(FORMATS) | grep ps > /dev/null; then export SRC="$$SRC $(DOC).ps"; fi; \ + if echo $(FORMATS) | grep pdf > /dev/null; then export SRC="$$SRC $(DOC).pdf"; fi; \ + \ + # upload releases to both X.Y/ and head/ subdirectories \ + export DIR=$(DOC_BASE)/gstreamer/$(PACKAGE_VERSION_MAJOR).$(PACKAGE_VERSION_MINOR)/$(DOC); \ + echo Uploading $$SRC to $(DOC_SERVER):$$DIR; \ + ssh $(DOC_SERVER) mkdir -p $$DIR; \ + rsync -rv -e ssh --delete $$SRC $(DOC_SERVER):$$DIR; \ + ssh $(DOC_SERVER) chmod -R g+w $$DIR; \ + \ + export DIR=$(DOC_BASE)/gstreamer/head/$(DOC); \ + echo Uploading $$SRC to $(DOC_SERVER):$$DIR; \ + ssh $(DOC_SERVER) mkdir -p $$DIR; \ + rsync -rv -e ssh --delete $$SRC $(DOC_SERVER):$$DIR; \ + ssh $(DOC_SERVER) chmod -R g+w $$DIR; \ + \ + if echo $(FORMATS) | grep html > /dev/null; then \ + echo "Un-preparing docs for upload (rebasing cross-references) ..." ; \ + gtkdoc-rebase --html-dir=$(builddir)/html ; \ + fi; \ + echo Done diff --git a/validate/common/win32.mak b/validate/common/win32.mak new file mode 100644 index 0000000..30e347e --- /dev/null +++ b/validate/common/win32.mak @@ -0,0 +1,78 @@ +# various tests to make sure we dist the win32 stuff (for MSVC builds) right + +# the MANIFEST contains all win32 related files that should be disted +win32 = $(shell cat $(top_srcdir)/win32/MANIFEST) + +# wildcard is apparently not portable to other makes, hence the use of find +# these are library .def files with the symbols to export +win32defs = $(shell find $(top_srcdir)/win32/common -name '*.def') + +# wildcard is apparently not portable to other makes, hence the use of find +# these are files that need to be disted with CRLF line endings: +win32crlf = $(shell find $(top_srcdir)/win32 -name '*.dsw' -o -name '*.dsp') + +win32-debug: + @echo; \ + echo win32 = $(win32); \ + echo; \ + echo win32defs = $(win32defs); \ + echo; \ + echo win32crlf = $(win32crlf); \ + echo + +win32-check-crlf: + @echo Checking win32 files for CR LF line endings ...; \ + fail=0 ; \ + for each in $(win32crlf) ; do \ + result=`perl -e 'print grep(/\r\n/,<>)' "$$each" | wc -l`; \ + if test "$$result" = 0 ; then \ + echo $$each must be fixed to have CRLF line endings ; \ + fail=1; \ + fi ; \ + done ; \ + exit $$fail + +# make sure all symbols we export on linux are defined in the win32 .def too +# (don't care about other unixes for now, it's enough if it works on one of +# the linux build bots; we assume .so ) +check-exports: + @fail=0 ; \ + for l in $(win32defs); do \ + libbase=`basename "$$l" ".def"`; \ + libso=`find "$(top_builddir)" -name "$$libbase-@GST_API_VERSION@.so" | grep -v /_build/ | head -n1`; \ + libdef="$(top_srcdir)/win32/common/$$libbase.def"; \ + if test "x$$libso" != "x"; then \ + echo Checking symbols in $$libso; \ + if ! ($(top_srcdir)/common/check-exports $$libdef $$libso) ; then \ + fail=1; \ + fi; \ + fi; \ + done ; \ + if test $$fail != 0; then \ + echo '-----------------------------------------------------------'; \ + echo 'Run this to update the .def files:'; \ + echo 'make update-exports'; \ + echo '-----------------------------------------------------------'; \ + fi; \ + exit $$fail + +update-exports: + make check-exports 2>&1 | patch -p1 + git add win32/common/libgst*.def + git diff --cached -- win32/common/ + echo '^^^--- updated and staged changes above' + +# complain about nonportable printf format strings (%lld, %llu, %zu etc.) +check-nonportable-print-format: + @fail=0 ; \ + loc=`find "$(top_srcdir)" -name '*.c' | xargs grep -n -e '%[0-9]*ll[udx]' -e '%[0-9]*z[udx]'`; \ + if test "x$$loc" != "x"; then \ + echo "Please fix the following print format strings:" ; \ + find "$(top_srcdir)" -name '*.c' | xargs grep -n -e '%[0-9]*ll[udx]' -e '%[0-9]*z[udx]'; \ + fail=1; \ + fi; \ + exit $$fail + +dist-hook: check-exports win32-check-crlf + + diff --git a/validate/configure.ac b/validate/configure.ac new file mode 100644 index 0000000..2db82ba --- /dev/null +++ b/validate/configure.ac @@ -0,0 +1,348 @@ +AC_PREREQ(2.62) +dnl initialize autoconf +dnl when going to/from release please set the nano (fourth number) right ! +dnl releases only do Wall, cvs and prerelease does Werror too +AC_INIT(Gst-Validate, 1.5.2.1, + http://bugzilla.gnome.org/enter_bug.cgi?product=GStreamer, + gst-validate) +AG_GST_INIT + +dnl initialize automake +AM_INIT_AUTOMAKE([-Wno-portability 1.11 no-dist-gzip dist-xz tar-ustar]) + +dnl define PACKAGE_VERSION_* variables +AS_VERSION + +dnl check if this is a release version +AS_NANO(GST_GIT="no", GST_GIT="yes") + +dnl can autoconf find the source ? +AC_CONFIG_SRCDIR([tools/gst-validate.c]) + +dnl define the output header for config +AC_CONFIG_HEADERS([config.h]) + +dnl AM_MAINTAINER_MODE only provides the option to configure to enable it +AM_MAINTAINER_MODE([enable]) + +dnl sets host_* variables +AC_CANONICAL_HOST + +dnl use pretty build output with automake >= 1.11 +m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])], + [AM_DEFAULT_VERBOSITY=1 + AC_SUBST(AM_DEFAULT_VERBOSITY)]) + +dnl our libraries and install dirs use major.minor as a version +dnl GST_API_VERSION=$PACKAGE_VERSION_MAJOR.$PACKAGE_VERSION_MINOR +dnl we override it here if we need to for the release candidate of new series +GST_API_VERSION=1.0 +AC_SUBST(GST_API_VERSION) +AC_DEFINE_UNQUOTED(GST_API_VERSION, "$GST_API_VERSION", + [GStreamer API Version]) + +AS_LIBTOOL(GST, 0, 0, 0) + +dnl *** required versions of GStreamer stuff *** +GST_REQ=1.0.0 +GSTPB_REQ=1.0.0 + +dnl *** autotools stuff **** + +dnl allow for different autotools +AS_AUTOTOOLS_ALTERNATE + +dnl Add parameters for aclocal +AC_SUBST(ACLOCAL_AMFLAGS, "-I m4 -I common/m4") +AC_CONFIG_MACRO_DIR([m4]) + +dnl set up gettext +dnl the version check needs to stay here because autopoint greps for it +AM_GNU_GETTEXT_VERSION([0.17]) +AM_GNU_GETTEXT([external]) +AG_GST_GETTEXT([gst-validate-$GST_API_VERSION]) + +dnl Check wether to build LDPRELOAD related code or not +AC_CANONICAL_HOST +case $host_os in + mingw* | msvc* | mks*) + BUILD_LDPRELOAD=no ;; + *) + BUILD_LDPRELOAD=yes ;; +esac +AM_CONDITIONAL(HAVE_LD_PRELOAD, test "x$BUILD_LDPRELOAD" = "xyes") + +dnl *** check for arguments to configure *** + +AG_GST_ARG_DEBUG +AG_GST_ARG_VALGRIND +AG_GST_ARG_GCOV +AG_GST_ARG_WITH_PACKAGE_NAME +AG_GST_ARG_WITH_PACKAGE_ORIGIN + +AG_GST_PKG_CONFIG_PATH + +dnl *** checks for platform *** + +dnl * hardware/architecture * + +dnl *** checks for programs *** + +dnl find a compiler +AC_PROG_CC +AM_PROG_CC_C_O + +AC_PATH_PROG(VALGRIND_PATH, valgrind, no) +AM_CONDITIONAL(HAVE_VALGRIND, test ! "x$VALGRIND_PATH" = "xno") + +dnl check for gobject-introspection +GOBJECT_INTROSPECTION_CHECK([0.6.3]) + +dnl check for documentation tools +AG_GST_DOCBOOK_CHECK +GTK_DOC_CHECK([1.3]) + + +AC_CHECK_PROG(enable_sphinx_doc, sphinx-build, yes, no) +AM_CONDITIONAL(HAVE_SPHINHX, test ! "x$enable_sphinx_doc" = "xno") + +dnl *** checks for libraries *** + +dnl *** checks for header files *** + +dnl *** checks for types/defines *** + +dnl *** checks for structures *** + +dnl *** checks for compiler characteristics *** + +dnl *** checks for library functions *** + +dnl *** checks for dependancy libraries *** + +dnl check for libm +LT_LIB_M +AC_SUBST(LIBM) + +dnl GLib is required +GLIB_REQ=2.36.0 +AC_SUBST([GLIB_REQ]) +AG_GST_GLIB_CHECK([$GLIB_REQ]) + +dnl checks for gstreamer +dnl uninstalled is selected preferentially -- see pkg-config(1) +AG_GST_CHECK_GST($GST_API_VERSION, [$GST_REQ], [yes]) + +GST_TOOLS_DIR=`$PKG_CONFIG --variable=toolsdir gstreamer-$GST_API_VERSION` +if test -z $GST_TOOLS_DIR; then + AC_MSG_ERROR([no tools dir defined in GStreamer pkg-config file; core upgrade needed.]) +fi +AC_SUBST(GST_TOOLS_DIR) + +GST_PLUGINS_DIR=`$PKG_CONFIG gstreamer-$GST_API_VERSION --variable pluginsdir` +AC_SUBST(GST_PLUGINS_DIR) +AC_MSG_NOTICE(Using GStreamer Core Plugins in $GST_PLUGINS_DIR) + +AG_GST_CHECK_GST_BASE($GST_API_VERSION, [$GST_REQ], [yes]) + +AG_GST_CHECK_GST_PLUGINS_BASE($GST_API_VERSION, [$GSTPB_REQ], [yes]) +GSTPB_PLUGINS_DIR=`$PKG_CONFIG gstreamer-plugins-base-$GST_API_VERSION --variable pluginsdir` +AC_SUBST(GSTPB_PLUGINS_DIR) +AC_MSG_NOTICE(Using GStreamer Base Plugins in $GSTPB_PLUGINS_DIR) + +dnl check for gstreamer-pbutils +PKG_CHECK_MODULES(GST_PBUTILS, gstreamer-pbutils-$GST_API_VERSION, HAVE_GST_PBUTILS="yes", HAVE_GST_PBUTILS="no") +if test "x$HAVE_GST_PBUTILS" != "xyes"; then + AC_ERROR([gst-pbutils is required]) +fi +AC_SUBST(GST_PBUTILS_LIBS) +AC_SUBST(GST_PBUTILS_CFLAGS) + +dnl check for gstreamer-video +PKG_CHECK_MODULES(GST_VIDEO, gstreamer-video-$GST_API_VERSION >= 1.4, HAVE_GST_VIDEO="yes", HAVE_GST_VIDEO="no") +if test "x$HAVE_GST_VIDEO" != "xyes"; then + AC_ERROR([gst-video is required]) +fi +AC_SUBST(GST_VIDEO_LIBS) +AC_SUBST(GST_VIDEO_CFLAGS) + +dnl needed for scenarios definition files +GST_PREFIX="`$PKG_CONFIG --variable=prefix gstreamer-$GST_API_VERSION`" +AC_SUBST(GST_PREFIX) +GST_DATADIR="$GST_PREFIX/share" +AC_DEFINE_UNQUOTED(GST_DATADIR, "$GST_DATADIR", [system wide data directory]) + +PKG_CHECK_MODULES(GIO, gio-2.0, HAVE_GIO=yes, HAVE_GIO=no) +AC_SUBST(GIO_CFLAGS) +AC_SUBST(GIO_LIBS) + +PKG_CHECK_MODULES(GTK, gtk+-3.0, HAVE_GTK=yes, HAVE_GTK=no) +AC_SUBST(GTK_CFLAGS) +AC_SUBST(GTK_LIBS) +AM_CONDITIONAL(HAVE_GTK, test "x$HAVE_GTK" = "xyes") + +PKG_CHECK_MODULES(GDK, gdk-3.0, HAVE_GDK=yes, HAVE_GDK=no) +AC_SUBST(GDK_CFLAGS) +AC_SUBST(GDK_LIBS) + +PKG_CHECK_MODULES(CAIRO, "cairo", HAVE_CAIRO=yes, HAVE_CAIRO=no) +AC_SUBST(CAIRO_CFLAGS) +AC_SUBST(CAIRO_LIBS) +AM_CONDITIONAL(HAVE_CAIRO, test ! "x$HAVE_CAIRO" = "xno") +if test "x$HAVE_CAIRO" != "xyes"; then + AC_MSG_NOTICE([Cairo is needed for the gst-validate-images-tool]) +fi + +dnl checks for gstreamer + +AG_GST_CHECK_GST_CHECK($GST_API_VERSION, [$GST_REQ], no) +AM_CONDITIONAL(HAVE_GST_CHECK, test "x$HAVE_GST_CHECK" = "xyes") + +dnl *** set variables based on configure arguments *** + +dnl set license and copyright notice +GST_LICENSE="LGPL" +AC_DEFINE_UNQUOTED(GST_LICENSE, "$GST_LICENSE", [GStreamer license]) +AC_SUBST(GST_LICENSE) + +dnl define location of plugin directory +AS_AC_EXPAND(PLUGINDIR, ${libdir}/gstreamer-$GST_API_VERSION/validate) +AC_DEFINE_UNQUOTED(PLUGINDIR, "$PLUGINDIR", +[directory where GstValidate plugins are located]) +AC_MSG_NOTICE([Using $PLUGINDIR as the plugin install location for GstValidate]) + +dnl plugin directory configure-time variable for use in Makefile.am +plugindir="\$(libdir)/gstreamer-$GST_API_VERSION/validate" +AC_SUBST(plugindir) + +# set by AG_GST_PARSE_SUBSYSTEM_DISABLES above +dnl make sure it doesn't complain about unused variables if debugging is disabled +NO_WARNINGS="" +AG_GST_CHECK_GST_DEBUG_DISABLED([NO_WARNINGS="-Wno-unused"], [NO_WARNINGS=""]) + +dnl define an ERROR_CFLAGS Makefile variable +AG_GST_SET_ERROR_CFLAGS($GST_GIT, [-Wmissing-declarations -Wmissing-prototypes -Wredundant-decls -Wundef -Wwrite-strings -Wformat-security -Wold-style-definition -Winit-self -Wmissing-include-dirs -Waddress -Waggregate-return -Wno-multichar -Wnested-externs $NO_WARNINGS]) + +dnl define correct level for debugging messages +AG_GST_SET_LEVEL_DEFAULT($GST_GIT) + +dnl used in examples +AG_GST_DEFAULT_ELEMENTS + +dnl *** finalize CFLAGS, LDFLAGS, LIBS + +dnl Overview: +dnl GST_OPTION_CFLAGS: common flags for profiling, debugging, errors, ... +dnl GST_*: flags shared by built objects to link against GStreamer +dnl GST_ALL_LDFLAGS: linker flags shared by all +dnl GST_LIB_LDFLAGS: additional linker flags for all libaries +dnl GST_LT_LDFLAGS: library versioning of our libraries +dnl GST_PLUGIN_LDFLAGS: flags to be used for all plugins + +dnl GST_OPTION_CFLAGS +if test "x$USE_DEBUG" = xyes; then + PROFILE_CFLAGS="-g" +fi +AC_SUBST(PROFILE_CFLAGS) + +DEPRECATED_CFLAGS="-DGST_DISABLE_DEPRECATED" +AC_SUBST(DEPRECATED_CFLAGS) + +dnl every flag in GST_OPTION_CFLAGS can be overridden at make time +GST_OPTION_CFLAGS="\$(WARNING_CFLAGS) \$(ERROR_CFLAGS) \$(DEBUG_CFLAGS) \$(PROFILE_CFLAGS) \$(GCOV_CFLAGS) \$(OPT_CFLAGS) \$(DEPRECATED_CFLAGS)" +AC_SUBST(GST_OPTION_CFLAGS) + +dnl FIXME: do we want to rename to GST_ALL_* ? +dnl prefer internal headers to already installed ones +dnl add GST_OPTION_CFLAGS, but overridable +GST_CFLAGS="$GST_CFLAGS \$(GST_OPTION_CFLAGS)" +AC_SUBST(GST_CFLAGS) +AC_SUBST(GST_LIBS) + +dnl GST_ALL_* +dnl vars common to for all internal objects (core libs, elements, applications) +dnl CFLAGS: +dnl - src and build dirs need to be added because every piece that gets built +dnl will need the GStreamer source and generated headers +GST_ALL_CFLAGS="-I\$(top_srcdir) -I\$(top_builddir) $GST_PLUGINS_BASE_CFLAGS $GST_CFLAGS \$(GST_OPTION_CFLAGS)" +AC_SUBST([GST_ALL_CFLAGS]) + +dnl FIXME: check if LTLIBINTL is needed everywhere +dnl I presume it is given that it contains the symbols that _() stuff maps to +GST_ALL_LIBS="$GST_LIBS $LTLIBINTL \$(GCOV_LIBS)" +AC_SUBST([GST_ALL_LIBS]) + +dnl LDFLAGS really should only contain flags, not libs - they get added before +dnl whatevertarget_LIBS and -L flags here affect the rest of the linking +GST_ALL_LDFLAGS="-no-undefined" +AC_SUBST(GST_ALL_LDFLAGS) + +dnl GST_LIB_LDFLAGS +dnl linker flags shared by all libraries +dnl LDFLAGS modifier defining exported symbols from built libraries +GST_LIB_LDFLAGS="-export-symbols-regex \^[_]?\(gst_\|Gst\|GST_\).*" +AC_SUBST(GST_LIB_LDFLAGS) + +dnl this really should only contain flags, not libs - they get added before +dnl whatevertarget_LIBS and -L flags here affect the rest of the linking +GST_PLUGIN_LDFLAGS="-module -avoid-version -export-symbols-regex '^[_]*gst_plugin_.*' $GST_ALL_LDFLAGS" +AC_SUBST(GST_PLUGIN_LDFLAGS) + +AM_PATH_PYTHON(2.7.0) +AS_AC_EXPAND(LIBDIR, $libdir) +AC_MSG_NOTICE(Storing library files in $LIBDIR) +AC_CONFIG_FILES([tools/gst-validate-launcher], [chmod +x tools/gst-validate-launcher]) +AS_AC_EXPAND(DATADIR, $datadir) +AC_CONFIG_FILES([launcher/config.py]) + +dnl this really should only contain flags, not libs - they get added before +dnl whatevertarget_LIBS and -L flags here affect the rest of the linking + +dnl *** output files *** + +dnl keep this alphabetic per directory, please +AC_CONFIG_FILES([ +Makefile +common/Makefile +common/m4/Makefile +data/Makefile +data/scenarios/Makefile +gst/Makefile +gst/validate/Makefile +gst/preload/Makefile +gst/overrides/Makefile +plugins/Makefile +plugins/fault_injection/Makefile +plugins/gapplication/Makefile +plugins/gtk/Makefile +plugins/ssim/Makefile +gst-libs/Makefile +gst-libs/gst/Makefile +gst-libs/gst/video/Makefile +tests/Makefile +tests/check/Makefile +pkgconfig/Makefile +pkgconfig/gst-validate-uninstalled.pc +pkgconfig/gst-validate.pc +po/Makefile.in +tools/Makefile +launcher/Makefile +launcher/apps/Makefile +docs/Makefile +docs/version.entities +docs/validate/Makefile +docs/plugins/Makefile +docs/launcher/Makefile +]) +AC_OUTPUT + +echo " + +Configuration + Version : ${VERSION} + Source code location : ${srcdir} + Prefix : ${prefix} + Compiler : ${CC} + +gst-validate configured. Type 'make' to build. +" diff --git a/validate/data/Makefile.am b/validate/data/Makefile.am new file mode 100644 index 0000000..cc7321a --- /dev/null +++ b/validate/data/Makefile.am @@ -0,0 +1,7 @@ +SUBDIRS = scenarios + +configdir=${datadir}/gstreamer-$(GST_API_VERSION)/validate/ +config_DATA = \ + valgrind.config + +EXTRA_DIST = valgrind.config diff --git a/validate/data/gstvalidate.supp b/validate/data/gstvalidate.supp new file mode 100644 index 0000000..e8562d1 --- /dev/null +++ b/validate/data/gstvalidate.supp @@ -0,0 +1,152 @@ +### This file contains either validate specific suppressions or bugs that we +### can't easily address because they are lower in the stack. +### All the other suppressions should be added ton common/gst.supp + +### Each set of suppression rules should be prefixed by either: +### - FIXED: if the bug/leak has been fixed upstream but we keep the rule +### because the fix may not be deployed yet (because it's lower in the +### stack and not in gst itself). +### - PENDING: if the bug/leak hasn't been fixed yet. In this case, please +### add an url to the bug report. + +# PENDING: https://bugs.freedesktop.org/show_bug.cgi?id=90073 +{ + Leak in mesa fixed with http://lists.freedesktop.org/archives/mesa-dev/2015-April/082101.html + Memcheck:Leak + fun:malloc + fun:read_packet + fun:_xcb_in_read + fun:_xcb_conn_wait + fun:wait_for_reply + fun:xcb_wait_for_reply + fun:dri3_open + fun:dri3_create_screen + fun:AllocAndFetchScreenConfigs + fun:__glXInitialize + fun:glXQueryVersion +} + +{ + Leak in mesa fixed with http://lists.freedesktop.org/archives/mesa-dev/2015-April/082100.html + Memcheck:Leak + ... + fun:get_render_node_from_id_path_tag + fun:loader_get_user_preferred_fd + fun:dri3_create_screen + fun:AllocAndFetchScreenConfigs + fun:__glXInitialize + fun:glXQueryVersion +} + +# FIXED +{ + Fixed in pixman master + Memcheck:Leak + fun:memalign + fun:allocate_and_init + fun:tls_get_addr_tail +} + +# PENDING: https://bugzilla.gnome.org/show_bug.cgi?id=748417 +{ + Ignore all the invalid memory errors from libvpx + Memcheck:Cond + obj:/usr/lib64/libvpx.so* +} + +{ + Ignore all the invalid memory errors from libvpx + Memcheck:Value8 + obj:/usr/lib64/libvpx.so* +} + +# PENDING: https://bugzilla.gnome.org/show_bug.cgi?id=747110 +{ + https://bugzilla.gnome.org/show_bug.cgi?id=747110 + Memcheck:Addr4 + ... + fun:aac_decode_frame_int + fun:aac_decode_frame +} + +# PENDING: https://bugs.freedesktop.org/show_bug.cgi?id=90194 +{ + https://bugs.freedesktop.org/show_bug.cgi?id=90194 + Memcheck:Param + ioctl(generic) + fun:ioctl + fun:drmIoctl + fun:drmPrimeHandleToFD +} + +# PENDING: https://bugzilla.gnome.org/show_bug.cgi?id=749232 +# x264 errors +{ + + Memcheck:Cond + ... + fun:x264_encoder_encode +} + +{ + + Memcheck:Value8 + ... + fun:x264_encoder_encode +} + +{ + + Memcheck:Cond + ... + fun:x264_lookahead_thread +} + +{ + + Memcheck:Value8 + ... + fun:x264_lookahead_thread +} + +{ + + Memcheck:Cond + ... + fun:x264_threadpool_thread +} + +{ + + Memcheck:Value8 + obj:/usr/lib64/libx264.so.* +} + +{ + + Memcheck:Cond + obj:/usr/lib64/libx264.so.* +} + +# PENDING: https://bugzilla.gnome.org/show_bug.cgi?id=749428 +# Theora encoder + +{ + + Memcheck:Value8 + ... + fun:theora_enc_handle_frame +} + +{ + + Memcheck:Cond + ... + fun:theora_enc_handle_frame +} + +{ + + Memcheck:Value8 + fun:oc_enc_tokenize_ac +} diff --git a/validate/data/scenarios/Makefile.am b/validate/data/scenarios/Makefile.am new file mode 100644 index 0000000..020c8cf --- /dev/null +++ b/validate/data/scenarios/Makefile.am @@ -0,0 +1,52 @@ +scenariosdir=${datadir}/gstreamer-$(GST_API_VERSION)/validate/scenarios +scenarios_DATA = simple_seeks.scenario \ + seek_forward.scenario \ + seek_backward.scenario \ + seek_forward_backward.scenario \ + reverse_playback.scenario \ + fast_forward.scenario \ + fast_backward.scenario \ + alternate_fast_backward_forward.scenario \ + pause_resume.scenario \ + scrub_forward_seeking.scenario \ + scrub_backward_seeking.scenario \ + scrub_forward_seeking_full.scenario \ + scrub_backward_seeking_full.scenario \ + adaptive_video_size.scenario \ + adaptive_video_framerate.scenario \ + adaptive_video_framerate_size.scenario\ + force_key_unit.scenario\ + seek_with_stop.scenario\ + switch_audio_track_while_paused.scenario\ + switch_subtitle_track.scenario\ + switch_subtitle_track_while_paused.scenario\ + disable_subtitle_track_while_paused.scenario\ + change_state_intensive.scenario\ + play_15s.scenario \ + switch_audio_track.scenario + +EXTRA_DIST = simple_seeks.scenario \ + seek_forward.scenario \ + seek_backward.scenario \ + seek_forward_backward.scenario \ + reverse_playback.scenario \ + fast_forward.scenario \ + fast_backward.scenario \ + alternate_fast_backward_forward.scenario \ + pause_resume.scenario \ + scrub_forward_seeking.scenario \ + scrub_backward_seeking.scenario \ + scrub_forward_seeking_full.scenario \ + scrub_backward_seeking_full.scenario \ + adaptive_video_size.scenario \ + adaptive_video_framerate.scenario \ + adaptive_video_framerate_size.scenario\ + force_key_unit.scenario\ + seek_with_stop.scenario\ + switch_audio_track_while_paused.scenario\ + switch_subtitle_track.scenario\ + switch_subtitle_track_while_paused.scenario\ + disable_subtitle_track_while_paused.scenario\ + play_15s.scenario \ + change_state_intensive.scenario\ + switch_audio_track.scenario diff --git a/validate/data/scenarios/adaptive_video_framerate.scenario b/validate/data/scenarios/adaptive_video_framerate.scenario new file mode 100644 index 0000000..a3043af --- /dev/null +++ b/validate/data/scenarios/adaptive_video_framerate.scenario @@ -0,0 +1,5 @@ +description, duration=15.0 +set-restriction, playback-time=5.0, restriction-caps="video/x-raw,framerate=(fraction)5/1" +set-restriction, playback-time=10.0, restriction-caps="video/x-raw,framerate=(fraction)30/1" +eos, playback-time=15.0 +stop, playback-time=15.0 diff --git a/validate/data/scenarios/adaptive_video_framerate_size.scenario b/validate/data/scenarios/adaptive_video_framerate_size.scenario new file mode 100644 index 0000000..d5cf596 --- /dev/null +++ b/validate/data/scenarios/adaptive_video_framerate_size.scenario @@ -0,0 +1,7 @@ +description, duration=25.0 +set-restriction, playback-time=5.0, restriction-caps="video/x-raw,framerate=(fraction)5/1" +set-restriction, playback-time=10.0, restriction-caps="video/x-raw,height=20,width=20,framerate=(fraction)5/1" +set-restriction, playback-time=15.0, restriction-caps="video/x-raw,height=20,width=20,framerate=(fraction)30/1" +set-restriction, playback-time=20.0, restriction-caps="video/x-raw,height=720,width=1280,framerate=(fraction)30/1" +eos, playback-time=25.0 +stop, playback-time=25.0 diff --git a/validate/data/scenarios/adaptive_video_size.scenario b/validate/data/scenarios/adaptive_video_size.scenario new file mode 100644 index 0000000..c3b5d2e --- /dev/null +++ b/validate/data/scenarios/adaptive_video_size.scenario @@ -0,0 +1,5 @@ +description, duration=15.0 +set-restriction, playback-time=5.0, restriction-caps="video/x-raw,height=480,width=854" +set-restriction, playback-time=10.0, restriction-caps="video/x-raw,height=720,width=1280" +eos, playback-time=15.0 +stop, playback-time=15.0 diff --git a/validate/data/scenarios/alternate_fast_backward_forward.scenario b/validate/data/scenarios/alternate_fast_backward_forward.scenario new file mode 100644 index 0000000..328a96a --- /dev/null +++ b/validate/data/scenarios/alternate_fast_backward_forward.scenario @@ -0,0 +1,13 @@ +description, duration=55.0, min-media-duration=470.0, seek=true, reverse-playback=true +seek, name=backward-seek, playback-time=0.0, rate=-1.0, start=0.0, stop=310.0, flags=accurate+flush +seek, name=forward-seek, playback-time=305.0, rate=1.0, start=305.0, flags=accurate+flush +seek, name=Fast-forward-seek, playback-time=310.0, rate=2.0, start=310.0, flags=accurate+flush +seek, name=Fast-backward-seek, playback-time=320.0, rate=-2.0, start=0.0, stop=320.0, flags=accurate+flush +seek, name=Fast-forward-seek, playback-time=310.0, rate=4.0, start=310.0, flags=accurate+flush +seek, name=Fast-backward-seek, playback-time=330.0, rate=-4.0, start=0.0, stop=330.0, flags=accurate+flush +seek, name=Fast-forward-seek, playback-time=310.0, rate=8.0, start=310.0, flags=accurate+flush +seek, name=Fast-backward-seek, playback-time=350.0, rate=-8.0, start=0.0, stop=350.0, flags=accurate+flush +seek, name=Fast-forward-seek, playback-time=310.0, rate=16.0, start=310.0, flags=accurate+flush +seek, name=Fast-backward-seek, playback-time=390.0, rate=-16.0, start=0.0, stop=390.0, flags=accurate+flush +seek, name=Fast-forward-seek, playback-time=310.0, rate=32.0, start=310.0, flags=accurate+flush +seek, name=Fast-backward-seek, playback-time=470.0, rate=-32.0, start=310.0, stop=470.0, flags=accurate+flush diff --git a/validate/data/scenarios/camerabin_signal.scenario b/validate/data/scenarios/camerabin_signal.scenario new file mode 100644 index 0000000..55c0704 --- /dev/null +++ b/validate/data/scenarios/camerabin_signal.scenario @@ -0,0 +1,4 @@ +description, duration=20.0 +emit-signal, target-element-name=camerabin0, signal-name=start-capture, playback-time=10.0 +eos, playback-time=20.0 + diff --git a/validate/data/scenarios/change_state_intensive.scenario b/validate/data/scenarios/change_state_intensive.scenario new file mode 100644 index 0000000..cf628c4 --- /dev/null +++ b/validate/data/scenarios/change_state_intensive.scenario @@ -0,0 +1,3 @@ +description, duration=0, summary="Set state to NULL->PLAYING->NULL 20 times", need-clock-sync=true, min-media-duration=1.0 +set-state, state="null", sub-action="set-state, state=playing", repeat=40 +stop; diff --git a/validate/data/scenarios/disable_subtitle_track_while_paused.scenario b/validate/data/scenarios/disable_subtitle_track_while_paused.scenario new file mode 100644 index 0000000..abd8f6c --- /dev/null +++ b/validate/data/scenarios/disable_subtitle_track_while_paused.scenario @@ -0,0 +1,6 @@ +description, summary="Disable subtitle track while pipeline is PAUSED", min-subtitle-track=2, duration=5.0, handles-states=true +pause; +switch-track, name="Disable subtitle", type=text, disable=true +wait, duration=0.5 +play; +stop, playback-time=2.0 diff --git a/validate/data/scenarios/fast_backward.scenario b/validate/data/scenarios/fast_backward.scenario new file mode 100644 index 0000000..71f6f56 --- /dev/null +++ b/validate/data/scenarios/fast_backward.scenario @@ -0,0 +1,6 @@ +description, duration=30.0, minfo-media-duration=310.0, seek=true, reverse-playback=true, need-clock-sync=true, min-media-duration=310.0 +seek, name=Fast-backward-seek, playback-time=0.0, rate=-2.0, start=0.0, stop=310.0, flags=accurate+flush +seek, name=Fast-backward-seek, playback-time=300.0, rate=-4.0, start=0.0, stop=300.0, flags=accurate+flush +seek, name=Fast-backward-seek, playback-time=280.0, rate=-8.0, start=0.0, stop=280.0, flags=accurate+flush +seek, name=Fast-backward-seek, playback-time=240.0, rate=-16.0, start=0.0, stop=240.0, flags=accurate+flush +seek, name=Fast-backward-seek, playback-time=160.0, rate=-32.0, start=0.0, stop=160.0, flags=accurate+flush diff --git a/validate/data/scenarios/fast_forward.scenario b/validate/data/scenarios/fast_forward.scenario new file mode 100644 index 0000000..1f24383 --- /dev/null +++ b/validate/data/scenarios/fast_forward.scenario @@ -0,0 +1,7 @@ +description, duration=35.0, seek=true, need-clock-sync=true, min-media-duration=5.0 +seek, name=Fast-forward-seek, playback-time=0.0, rate=2.0, start=0.0, flags=accurate+flush +seek, name=Fast-forward-seek, playback-time="min(10.0, duration*0.25)", rate=4.0, start=0.0, flags=accurate+flush +seek, name=Fast-forward-seek, playback-time="min(20.0, duration*0.50)", rate=8.0, start=0.0, flags=accurate+flush +seek, name=Fast-forward-seek, playback-time="min(40.0, duration*0.75)", rate=16.0, start=0.0, flags=accurate+flush +seek, name=Fast-forward-seek, playback-time="min(50.0, duration*0.75)", rate=32.0, start=0.0, flags=accurate+flush +stop, playback-time="min(duration - 0.3, 60.0)" diff --git a/validate/data/scenarios/force_key_unit.scenario b/validate/data/scenarios/force_key_unit.scenario new file mode 100644 index 0000000..2a9c839 --- /dev/null +++ b/validate/data/scenarios/force_key_unit.scenario @@ -0,0 +1,4 @@ +description, duration=2.0 +video-request-key-unit, playback-time=1.0, direction=upstream, running_time=-1.0, all-header=true, count=1 +eos, playback-time=2.0 +stop, playback-time=2.0 diff --git a/validate/data/scenarios/pause_resume.scenario b/validate/data/scenarios/pause_resume.scenario new file mode 100644 index 0000000..27bfc10 --- /dev/null +++ b/validate/data/scenarios/pause_resume.scenario @@ -0,0 +1,6 @@ +description, duration=14.0, min-media-duration=7.0 +pause, name=First-pause, playback-time=1.0, duration=1.0 +pause, name=Second-pause, playback-time=3.0, duration=5.0 +pause, name=Third-pause, playback-time=5.0, duration=1.0 +eos, name=Done-testing, playback-time=7.0 +stop, playback-time=7.0 diff --git a/validate/data/scenarios/play_15s.scenario b/validate/data/scenarios/play_15s.scenario new file mode 100644 index 0000000..af86cb3 --- /dev/null +++ b/validate/data/scenarios/play_15s.scenario @@ -0,0 +1,3 @@ +description, duration=15.0 +eos, playback-time=15.0 +stop, playback-time=15.0 diff --git a/validate/data/scenarios/reverse_playback.scenario b/validate/data/scenarios/reverse_playback.scenario new file mode 100644 index 0000000..1a103c6 --- /dev/null +++ b/validate/data/scenarios/reverse_playback.scenario @@ -0,0 +1,2 @@ +description, seek=true, reverse-playback=true +seek, name=Reverse-seek, playback-time=0.0, rate=-1.0, start=0.0, stop=duration, flags=accurate+flush diff --git a/validate/data/scenarios/scrub_backward_seeking.scenario b/validate/data/scenarios/scrub_backward_seeking.scenario new file mode 100644 index 0000000..866a536 --- /dev/null +++ b/validate/data/scenarios/scrub_backward_seeking.scenario @@ -0,0 +1,7 @@ +description, seek=true +pause, playback-time=0.0 +seek, playback-time=0.0, start="duration - 0.5", flags=accurate+flush +seek, playback-time=0.0, start=position-0.1, repeat="min(10, (duration - 0.6))/0.1", flags=accurate+flush +play, playback-time=0.0 +stop, playback-time=1.0 + diff --git a/validate/data/scenarios/scrub_backward_seeking_full.scenario b/validate/data/scenarios/scrub_backward_seeking_full.scenario new file mode 100644 index 0000000..90d27e6 --- /dev/null +++ b/validate/data/scenarios/scrub_backward_seeking_full.scenario @@ -0,0 +1,7 @@ +description, seek=true +pause, playback-time=0.0 +seek, playback-time=0.0, start="duration - 0.5", flags=accurate+flush +seek, playback-time=0.0, start=position-0.1, repeat="(duration - 0.6)/0.1", flags=accurate+flush +play, playback-time=0.0 +stop, playback-time=1.0 + diff --git a/validate/data/scenarios/scrub_forward_seeking.scenario b/validate/data/scenarios/scrub_forward_seeking.scenario new file mode 100644 index 0000000..e7b16ff --- /dev/null +++ b/validate/data/scenarios/scrub_forward_seeking.scenario @@ -0,0 +1,5 @@ +description, seek=true, handles-states=true +pause, playback-time=0.0 +seek, playback-time=0.0, start=position+0.1, repeat="min(10, (duration - 0.5))/0.1", flags=accurate+flush +play, playback-time=0.0 +stop, playback-time=1.0 diff --git a/validate/data/scenarios/scrub_forward_seeking_full.scenario b/validate/data/scenarios/scrub_forward_seeking_full.scenario new file mode 100644 index 0000000..4e3b543 --- /dev/null +++ b/validate/data/scenarios/scrub_forward_seeking_full.scenario @@ -0,0 +1,5 @@ +description, seek=true, handles-states=true +pause, playback-time=0.0 +seek, playback-time=0.0, start=position+0.1, repeat="(duration - 0.5)/0.1", flags=accurate+flush +play, playback-time=0.0 +stop, playback-time=1.0 diff --git a/validate/data/scenarios/seek_backward.scenario b/validate/data/scenarios/seek_backward.scenario new file mode 100644 index 0000000..9cd2df3 --- /dev/null +++ b/validate/data/scenarios/seek_backward.scenario @@ -0,0 +1,5 @@ +description, seek=true, duration=30, need-clock-sync=true +seek, name=Backward-seek, playback-time="min(5.0, (duration/4))", rate=1.0, start=0.0, flags=accurate+flush +seek, name=Backward-seek, playback-time="min(10.0, 2*(duration/4))", rate=1.0, start="min(5.0, duration/4)", flags=accurate+flush +seek, name=Backward-seek, playback-time="min(15.0, 3*(duration/4))", rate=1.0, start="min(10.0, 2*(duration/4))", flags=accurate+flush +stop, playback-time="min(15.0, 3*(duration/4))" diff --git a/validate/data/scenarios/seek_forward.scenario b/validate/data/scenarios/seek_forward.scenario new file mode 100644 index 0000000..1b907c8 --- /dev/null +++ b/validate/data/scenarios/seek_forward.scenario @@ -0,0 +1,5 @@ +description, seek=true, duration=20, need-clock-sync=true +seek, name=First-forward-seek, playback-time="min(5.0, (duration/8))", start="min(10, 2*(duration/8))", flags=accurate+flush +seek, name=Second-forward-seek, playback-time="min(15.0, 3*(duration/8))", start="min(20, 4*(duration/8))", flags=accurate+flush +seek, name=Third-forward-seek, playback-time="min(25, 5*(duration/8))", start="min(30.0, 6*(duration/8))", flags=accurate+flush +stop, playback-time=35.0 diff --git a/validate/data/scenarios/seek_forward_backward.scenario b/validate/data/scenarios/seek_forward_backward.scenario new file mode 100644 index 0000000..e7bd0dc --- /dev/null +++ b/validate/data/scenarios/seek_forward_backward.scenario @@ -0,0 +1,9 @@ +description, seek=true, duration=40, min-media-duration=45.0 +seek, name=Forward-seek, playback-time=0.0, rate=1.0, start=5.0, flags=accurate+flush +seek, name=Backward-seek, playback-time=10.0, rate=1.0, start=0.0, flags=accurate+flush +seek, name=Backward-seek, playback-time=5.0, rate=1.0, start=25.0, stop=-1, flags=accurate+flush +seek, name=Backward-seek, playback-time=30.0, rate=1.0, start=0.0, flags=accurate+flush +seek, name=Forward-seek, playback-time=5.0, rate=1.0, start=15.0, flags=accurate+flush +seek, name=Forward-seek, playback-time=20.0, rate=1.0, start=35.0, flags=accurate+flush +seek, name=Backward-seek, playback-time=40.0, rate=1.0, start=25.0, flags=accurate+flush +seek, name=Last-backward-seek, playback-time=30.0, rate=1.0, start=5.0, stop=10.0, flags=accurate+flush diff --git a/validate/data/scenarios/seek_with_stop.scenario b/validate/data/scenarios/seek_with_stop.scenario new file mode 100644 index 0000000..5d70358 --- /dev/null +++ b/validate/data/scenarios/seek_with_stop.scenario @@ -0,0 +1,2 @@ +description, seek=true, duration=5.0, need_clock_sync=true, min-media-duration=2 +seek, playback-time=1.0, start=0.0, stop="min(5.0, duration-1.0)", flags=accurate+flush diff --git a/validate/data/scenarios/setup_sink_props_max_lateness.scenario b/validate/data/scenarios/setup_sink_props_max_lateness.scenario new file mode 100644 index 0000000..6d59510 --- /dev/null +++ b/validate/data/scenarios/setup_sink_props_max_lateness.scenario @@ -0,0 +1,3 @@ +description, is-config=true +set-property, target-element-klass=Sink/Video, property-name=max-lateness, property-value=600, optional=true +set-property, target-element-klass=Sink/Audio, property-name=max-lateness, property-value=600, optional=true diff --git a/validate/data/scenarios/simple_seeks.scenario b/validate/data/scenarios/simple_seeks.scenario new file mode 100644 index 0000000..645253a --- /dev/null +++ b/validate/data/scenarios/simple_seeks.scenario @@ -0,0 +1,4 @@ +description, seek=true, duration=5.0 +seek, playback-time=1.0, rate=1.0, start=2.0, flags=accurate+flush +seek, playback-time=3.0, rate=1.0, start=0.0, flags=accurate+flush +seek, playback-time=1.0, rate=1.0, start=2.0, stop=3.0, flags=accurate+flush diff --git a/validate/data/scenarios/switch_audio_track.scenario b/validate/data/scenarios/switch_audio_track.scenario new file mode 100644 index 0000000..5669d6e --- /dev/null +++ b/validate/data/scenarios/switch_audio_track.scenario @@ -0,0 +1,3 @@ +description, summary="Change audio track at 5 second to the second audio track", min-audio-track=2, duration=10.0 +switch-track, name=Next-audio-track, playback-time=5.0, type=audio, index=(string)+1 +stop, playback-time=10.0 diff --git a/validate/data/scenarios/switch_audio_track_while_paused.scenario b/validate/data/scenarios/switch_audio_track_while_paused.scenario new file mode 100644 index 0000000..30fc6b6 --- /dev/null +++ b/validate/data/scenarios/switch_audio_track_while_paused.scenario @@ -0,0 +1,11 @@ +description, summary="Change audio track while pipeline is paused", min-audio-track=2, duration=6.0, need-clock-sync=true +pause, playback-time=1.0; + +# Wait so that humans can see the pipeline is paused +wait, duration=0.5 +switch-track, name=Next-audio-track, type=audio, index=(string)+1 + +# Wait so that humans can see the pipeline is paused +wait, duration=0.5 +play; +stop, playback-time=5.0 diff --git a/validate/data/scenarios/switch_set_external_subtitle.scenario b/validate/data/scenarios/switch_set_external_subtitle.scenario new file mode 100644 index 0000000..7f92217 --- /dev/null +++ b/validate/data/scenarios/switch_set_external_subtitle.scenario @@ -0,0 +1 @@ +description, summary="Change subtitle track at 1 second while paused", duration=5.0, needs-ext-file="subtitles/%s.1.srt:subtitles/%s.1.srt" diff --git a/validate/data/scenarios/switch_subtitle_track.scenario b/validate/data/scenarios/switch_subtitle_track.scenario new file mode 100644 index 0000000..83ce3d6 --- /dev/null +++ b/validate/data/scenarios/switch_subtitle_track.scenario @@ -0,0 +1,3 @@ +description, summary="Change subtitle track at 1 second while playing back", min-subtitle-track=2, duration=5.0 +switch-track, playback-time=1.0, type=text, index=(string)+1 +stop, playback-time=5.0 diff --git a/validate/data/scenarios/switch_subtitle_track_while_paused.scenario b/validate/data/scenarios/switch_subtitle_track_while_paused.scenario new file mode 100644 index 0000000..15c4ec7 --- /dev/null +++ b/validate/data/scenarios/switch_subtitle_track_while_paused.scenario @@ -0,0 +1,7 @@ +description, summary="Change subtitle track while pipeline is PAUSED", min-subtitle-track=2, duration=5.0, handles-states=true +pause; +wait, duration=0.5 +switch-track, type=text, index=(string)+1 +wait, duration=0.5 +play; +stop, playback-time=5.0 diff --git a/validate/data/scenarios/update_start.scenario b/validate/data/scenarios/update_start.scenario new file mode 100644 index 0000000..54456fd --- /dev/null +++ b/validate/data/scenarios/update_start.scenario @@ -0,0 +1,2 @@ +description, summary="Use the set seek type to seek at 5 seconds after 2 seconds", seek=true +seek, playback-time=2.0, rate=1.0, start_type=set, start=5.0, stop_type=none, stop=0.0, flags=accurate+flush diff --git a/validate/data/scenarios/update_stop.scenario b/validate/data/scenarios/update_stop.scenario new file mode 100644 index 0000000..cb237d9 --- /dev/null +++ b/validate/data/scenarios/update_stop.scenario @@ -0,0 +1,3 @@ +description, summary="Use the set seek type to seek at 0 secs stop 10secs after 5 secs", seek=true +description, duration=15.0, seeks=true +seek, playback-time=5.0, rate=1.0, start_type=none, start=0.0, stop_type=set, stop=10.0, flags=accurate+flush diff --git a/validate/data/valgrind.config b/validate/data/valgrind.config new file mode 100644 index 0000000..bcccdd2 --- /dev/null +++ b/validate/data/valgrind.config @@ -0,0 +1 @@ +core, action=set-property, target-element-klass=Filter, property-name=qos, property-value=false diff --git a/validate/docs/.gitignore b/validate/docs/.gitignore new file mode 100644 index 0000000..54681d0 --- /dev/null +++ b/validate/docs/.gitignore @@ -0,0 +1,10 @@ +Makefile +Makefile.in +*.o +*.lo +*.la +.deps +.libs +version.entities +tmpl/ + diff --git a/validate/docs/Makefile.am b/validate/docs/Makefile.am new file mode 100644 index 0000000..6e69206 --- /dev/null +++ b/validate/docs/Makefile.am @@ -0,0 +1,13 @@ +SUBDIRS = validate plugins +DIST_SUBDIRS = validate launcher plugins + +if HAVE_SPHINHX +SUBDIRS += launcher +endif + +upload: + @if test "x$(SUBDIRS)" != x; then for a in $(SUBDIRS); do cd $$a; make upload; cd ..; done; fi + +libs: validate + +include $(top_srcdir)/common/parallel-subdirs.mak diff --git a/validate/docs/launcher/Makefile.am b/validate/docs/launcher/Makefile.am new file mode 100644 index 0000000..e7709a1 --- /dev/null +++ b/validate/docs/launcher/Makefile.am @@ -0,0 +1,177 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = . + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf html/* + -rm -rf dirhtml/* + -rm -rf singlehtml/* + -rm -rf pickle/* + -rm -rf json/* + -rm -rf htmlhelp/* + -rm -rf qthelp/* + -rm -rf devhelp/* + -rm -rf epub/* + -rm -rf latex/* + -rm -rf text/* + -rm -rf man/* + -rm -rf texinfo/* + -rm -rf info/* + -rm -rf gettext/* + -rm -rf changes/* + -rm -rf linkcheck/* + -rm -rf doctest/* + +all: + @export PYTHONPATH=$(PYTHONPATH):$(top_srcdir)/tools/launcher:$(top_srcdir)/tools/launcher/apps + @echo $(PYTHONPATH) + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/gst-validate-launcher.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/gst-validate-launcher.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/gst-validate-launcher" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/gst-validate-launcher" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +# for upload-doc.mak +DOC=gst-validate-launcher +FORMATS=html +include $(top_srcdir)/common/upload-doc.mak diff --git a/validate/docs/launcher/conf.py b/validate/docs/launcher/conf.py new file mode 100644 index 0000000..dadd75a --- /dev/null +++ b/validate/docs/launcher/conf.py @@ -0,0 +1,243 @@ +# -*- coding: utf-8 -*- +# +# gst-validate-launcher documentation build configuration file, created by +# sphinx-quickstart on Sat Sep 6 11:26:51 2014. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.coverage'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'gst-validate-launcher' +copyright = u'2014, Thibault Saunier' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '1.0.0.1' +# The full version, including alpha/beta/rc tags. +release = '1.0.0.1' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'gst-validate-launcherdoc' + + +# -- Options for LaTeX output -------------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'gst-validate-launcher.tex', u'gst-validate-launcher Documentation', + u'Thibault Saunier', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'gst-validate-launcher', u'gst-validate-launcher Documentation', + [u'Thibault Saunier'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------------ + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'gst-validate-launcher', u'gst-validate-launcher Documentation', + u'Thibault Saunier', 'gst-validate-launcher', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' +autoclass_content = 'both' diff --git a/validate/docs/launcher/index.rst b/validate/docs/launcher/index.rst new file mode 100644 index 0000000..e32d427 --- /dev/null +++ b/validate/docs/launcher/index.rst @@ -0,0 +1,22 @@ +.. gst-validate-launcher documentation master file, created by + sphinx-quickstart on Sat Sep 6 11:26:51 2014. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to gst-validate-launcher's documentation! +================================================= + +Contents: + +.. toctree:: + :maxdepth: 2 + + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + diff --git a/validate/docs/launcher/launcher.rst b/validate/docs/launcher/launcher.rst new file mode 100644 index 0000000..f2991cb --- /dev/null +++ b/validate/docs/launcher/launcher.rst @@ -0,0 +1,43 @@ +launcher Package +================ + +:mod:`launcher` Package +----------------------- + +.. automodule:: __init__ + :members: + :undoc-members: + :show-inheritance: + +:mod:`baseclasses` Module +------------------------- + +.. automodule:: baseclasses + :members: + :undoc-members: + :show-inheritance: + +:mod:`reporters` Module +----------------------- + +.. automodule:: reporters + :members: + :undoc-members: + :show-inheritance: + +:mod:`utils` Module +------------------- + +.. automodule:: utils + :members: + :undoc-members: + :show-inheritance: + +:mod:`gstvalidate` Module +------------------- + +.. automodule:: gstvalidate + :members: + :undoc-members: + :show-inheritance: + diff --git a/validate/docs/launcher/modules.rst b/validate/docs/launcher/modules.rst new file mode 100644 index 0000000..1654d7e --- /dev/null +++ b/validate/docs/launcher/modules.rst @@ -0,0 +1,7 @@ +. += + +.. toctree:: + :maxdepth: 4 + + launcher diff --git a/validate/docs/plugins/Makefile.am b/validate/docs/plugins/Makefile.am new file mode 100644 index 0000000..4986c2c --- /dev/null +++ b/validate/docs/plugins/Makefile.am @@ -0,0 +1,62 @@ +GST_DOC_SCANOBJ = $(top_srcdir)/common/gstdoc-scangobj + +# The name of the module, e.g. 'glib'. +MODULE=gst-validate +DOC_MODULE=$(MODULE)-plugins + +# for upload-doc.mak +DOC=$(MODULE)-plugins +FORMATS=html +html: html-build.stamp +include $(top_srcdir)/common/upload-doc.mak + +# The top-level SGML file. Change it if you want. +DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml + +# The directory containing the source code. +# gtk-doc will search all .c & .h files beneath here for inline comments +# documenting functions and macros. +DOC_SOURCE_DIR = $(top_srcdir)/plugins/ + +# Extra options to supply to gtkdoc-scan. +SCAN_OPTIONS= + +# Extra options to supply to gtkdoc-mkdb. +MKDB_OPTIONS=--sgml-mode + +# Extra options to supply to gtkdoc-fixref. +FIXXREF_OPTIONS=--extra-dir=$(top_builddir)/docs/gst/html \ + --extra-dir=$(top_builddir)/docs/libs/html \ + --extra-dir=$(GLIB_PREFIX)/share/gtk-doc/html \ + --extra-dir=$(datadir)/gtk-doc/html + +# Used for dependencies. +CFILE_GLOB=$(top_srcdir)/plugins/*/*.c + +# Header files to ignore when scanning. +IGNORE_HFILES = +IGNORE_CFILES = + +# Images to copy into HTML directory. +HTML_IMAGES = + +# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE). +content_files = + +# Other files to distribute. +extra_files = + +# CFLAGS and LDFLAGS for compiling scan program. Only needed if your app/lib +# contains GtkObjects/GObjects and you want to document signals and properties. +GTKDOC_CFLAGS = $(GST_BASE_CFLAGS) $(GST_OBJ_CFLAGS) -I$(top_builddir) -I$(top_builddir)/gst-libs +GTKDOC_LIBS = $(GST_BASE_LIBS) + +GTKDOC_CC=$(LIBTOOL) --tag=CC --mode=compile $(CC) +GTKDOC_LD=$(LIBTOOL) --tag=CC --mode=link $(CC) + +# If you need to override some of the declarations, place them in this file +# and uncomment this line. +DOC_OVERRIDES = $(DOC_MODULE)-overrides.txt + +include $(top_srcdir)/common/gtk-doc.mak + diff --git a/validate/docs/plugins/gst-validate-plugins-docs.sgml b/validate/docs/plugins/gst-validate-plugins-docs.sgml new file mode 100644 index 0000000..42dddd4 --- /dev/null +++ b/validate/docs/plugins/gst-validate-plugins-docs.sgml @@ -0,0 +1,28 @@ + + +%version-entities; +]> + + + + GStreamer Validate Plugins &GST_API_VERSION; Plugins Reference Manual + + GStreamer Validate Plugins &GST_API_VERSION; Plugins Reference Manual + for GStreamer Validate &GST_API_VERSION; (&GST_VERSION;) + The latest version of this documentation can be found on-line at + + http://gstreamer.freedesktop.org/data/doc/gstreamer/head/gst-validate-plugins/html/ + . + + + + + GstValidate plugins + + + + + diff --git a/validate/docs/plugins/gst-validate-plugins-overrides.txt b/validate/docs/plugins/gst-validate-plugins-overrides.txt new file mode 100644 index 0000000..e69de29 diff --git a/validate/docs/plugins/gst-validate-plugins-sections.txt b/validate/docs/plugins/gst-validate-plugins-sections.txt new file mode 100644 index 0000000..242e4eb --- /dev/null +++ b/validate/docs/plugins/gst-validate-plugins-sections.txt @@ -0,0 +1,9 @@ +
+validate-ssim +Validate SSim plugin +VALIDATE_TYPE_S_SIM_OVERRIDE +ValidateSSimOverrideClass +validate_s_sim_override_new +ValidateSSimOverride +
+ diff --git a/validate/docs/plugins/gst-validate-plugins.sgml b/validate/docs/plugins/gst-validate-plugins.sgml new file mode 100644 index 0000000..38736dd --- /dev/null +++ b/validate/docs/plugins/gst-validate-plugins.sgml @@ -0,0 +1,24 @@ + + +%version-entities; +]> + + + + GStreamer Validate Plugins &GST_API_VERSION; Plugins Reference Manual + + for GStreamer Validate &GST_API_VERSION; (&GST_VERSION;) + The latest version of this documentation can be found on-line at + http://gstreamer.freedesktop.org/data/doc/gstreamer/head/gst-validate/html/. + + + + + gst-validate plugins + + + + + diff --git a/validate/docs/plugins/gst-validate-plugins.types b/validate/docs/plugins/gst-validate-plugins.types new file mode 100644 index 0000000..9f4950e --- /dev/null +++ b/validate/docs/plugins/gst-validate-plugins.types @@ -0,0 +1 @@ +#include diff --git a/validate/docs/release.txt b/validate/docs/release.txt new file mode 100644 index 0000000..d2d83da --- /dev/null +++ b/validate/docs/release.txt @@ -0,0 +1,2 @@ +To be able to use the new-release script from www/bin/ you will need to link $GST/gst-devtools/validate to +$GST/gst-validate/ -- After doing that you can follow the standard GStreamer releasing procedure. diff --git a/validate/docs/validate-design.txt b/validate/docs/validate-design.txt new file mode 100644 index 0000000..04cb31c --- /dev/null +++ b/validate/docs/validate-design.txt @@ -0,0 +1,61 @@ + +== Main components + +Gst-validate is composed of 4 parts: the issues, the reports, the runner and +the reporters. + += Issue +Gst-Validate's main target is finding problems in GStreamer elements/pipelines. +To make it easier to track down exactly what happens, the tests run by +Gst-Validate use an extensible list of 'Issues'. Each Issue describes a +potential error situation and has an unique ID and a severity level. + +The issues list can be extended by 3rd party libraries if specific needs +should be met. + += Reporters +A reporter is the object that implements the GstValidateReporter interface and +is responsible for performing tests on some target element/scenario. The +reporter is able to create 'Reports' whenever a test it executes fails. + += Reports +The GstValidateReports are created whenever a test fails, they are posted to the +stderr and also are posted to the GstValidateRunner for accumulation. + +Each report contains information about the object that generated the issue, +the issue associated with the report and a specific debug message for the case, +this helps tracking down the problem and fixing it. + += Runner +The GstValidateRunner is the point of communication for the app to gst-validate +monitoring. It provides an API to gather reports and to make them accessible +to the application. + +== Reporter types + += Monitors +The monitors are used to wrap around pipelines (and elements and pads) and +attach to their data flow handling functions to be able to intercept the data +and compare it with the expected behaviors. There are 3 types of monitors: + + * GstValidateElementMonitor + * GstValidateBinMonitor + * GstValidatePadMonitor + +All 3 inherit from the base GstValidateMonitor class. Their name suggest what +they monitor and they have a relationship to their children and parents. A bin +monitor has, possibly, child element monitors and element monitors have child +pad monitors. The monitors are responsible for listening to new children added +to their monitored object and creating monitors for them. For example, element +monitors listen to element's pad-added signal and create pad monitors whenever +a new pad is added. + +Most (if not all) the checks are implemented at the GstValidatePadMonitor, +as it is where the data flow happens. + += FileChecker +The file checker is another reporter that is used to make sure a file has a +few expected properties. It inspects the file and compares the results with +expected values set by the user. Values such as file duration, file size, if +it can be played back and also if its encoding and container types. + diff --git a/validate/docs/validate-usage.txt b/validate/docs/validate-usage.txt new file mode 100644 index 0000000..44df3d7 --- /dev/null +++ b/validate/docs/validate-usage.txt @@ -0,0 +1,90 @@ +=== The GstValidate CLI Tools + +The commands here assume that you have installed gst-validate. If this +is not the case, go into the gst-validate directory and call the tools +directly with the path tools/ + +1- gst-validate-1.0: It is the simplest tool and is used to run a gst +launch style pipeline. Monitors are added to it to identify issues in the +used elements. At the end a report will be printed, this report will +contain information about all issues that were encountered while running +gst-validate. To view issues as they are created, set the environment +variable GST_DEBUG=validate:2 and it will be printed as gstreamer +debugging. You can basically run any GstPipeline pipeline using it. +If you are not familiar with gst-launch syntax, please refer to +gst-launch's documentation. + +Examples: + + # Simple playback pipeline + gst-validate-1.0 playbin uri=file:///path/to/some/media/file + + # Transcoding pipeline + gst-validate-1.0 filesrc location=/root/Videos/big_buck_bunny_1080p_h264.mov ! \ + qtdemux name=d ! queue ! x264enc ! h264parse ! mpegtsmux name=m ! progressreport ! filesink location=/root/test.ts \ + d. ! queue ! faac ! m. + +You can also activate what we call "scenarios" which will execute +actions on the pipeline. Those actions can be for example, "set pipeline +to pause", "seek to N with rate=x" etc, using the following syntax: + + gst-validate-1.0 playbin uri=file:///path/to/some/media/file --set-scenario=seek_forward + +You can list all available scenarios using: + + gst-validate-transcoding-1.0 --list-scenarios + +Scenarios are simple text files describing a list of actions, you can find the +source scenario files in validate/data/ + +You can find more information about scenarios on the GstValidateScenario documentation: http://gstreamer.freedesktop.org/data/doc/gstreamer/head/gst-validate/html/GstValidateScenario.html + +You can find more information about the various action types available to be executed with: + + gst-validate-1.0 -t + +or: + + gst-validate-transcoding-1.0 -t + + 2- gst-validate-transcoding-1.0: Transcodes input-uri to output-uri, +using the given encoding profile. The pipeline will be monitored for +possible issues detection using the gst-validate lib, at the end of +execution, a report containing information about all found issues will +be printed. + +Example: + + # Will transcode file://path/to/some/media/file to H264/AAC into mp4 + gst-validate-transcoding-1.0 -o 'video/quicktime,variant=iso:video/x-h264:audio/mpeg,mpegversion=4' \ + file://path/to/some/media/file file:///path/to/destination_h264_aac.qt + +The same scenarios can be activated on gst-validate-transcoding-1.0 as +with gst-validate-1.0 + + 3- gst-validate-media-check-1.0: Analyzes a media file and writes the +results to stdout or a file. It can also compare the results found with +another results file for identifying regressions. The monitoring lib +from gst-validate will be enabled during the tests to identify issues +with the GStreamer elements involved with the media file's container and +codec types. It will actually do a series of checks over the media file. + +Example: + + # Will check various media properties from the file + gst-validate-media-check-1.0 file://path/to/some/media/file + +=== LD_PRELOAD / Testing with exiting application + +If you want to test an already existing application without modifying it. Just +use: + +LD_PRELOAD=path/to/libgstvalidatepreload.so yourapp ... + +gst-validate will try to replace GstPipeline creating functions and configure +monitors automatically for you, reports will be printed to stderr when +they are found. You can also use GST_DEBUG to view the issues that were found + +NOTES: The exit code will be "18" in case a critical issue has +been seen while running any of those tools. + diff --git a/validate/docs/validate/.gitignore b/validate/docs/validate/.gitignore new file mode 100644 index 0000000..2ac8cbf --- /dev/null +++ b/validate/docs/validate/.gitignore @@ -0,0 +1,6 @@ +*.bak +*.stamp +html +xml +Makefile +Makefile.in diff --git a/validate/docs/validate/Makefile.am b/validate/docs/validate/Makefile.am new file mode 100644 index 0000000..a9eaaff --- /dev/null +++ b/validate/docs/validate/Makefile.am @@ -0,0 +1,101 @@ +GST_DOC_SCANOBJ = $(top_srcdir)/common/gstdoc-scangobj +## Process this file with automake to produce Makefile.in + +# The name of the module, e.g. 'glib'. +MODULE=gst-validate +DOC_MODULE=gst-validate + +# don't want $(DOC_MODULE)-scan.c to be built with -Werror +ERROR_CFLAGS= + +# for upload-doc.mak +DOC=gst-validate +FORMATS=html +html: html-build.stamp +include $(top_srcdir)/common/upload-doc.mak + +# The top-level SGML file. Change it if you want. +DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml + +# The directory containing the source code. +# gtk-doc will search all .c & .h files beneath here for inline comments +# documenting functions and macros. +DOC_SOURCE_DIR=$(top_srcdir)/gst/validate/ + +# Extra options to supply to gtkdoc-scan. +SCAN_OPTIONS=--deprecated-guards="GST_DISABLE_DEPRECATED" + +# Extra options to supply to gtkdoc-mkdb. +MKDB_OPTIONS=--sgml-mode --output-format=xml + +# Extra options to supply to gtkdoc-fixref. +FIXXREF_OPTIONS=--extra-dir=$(top_builddir)/docs/gst/html \ + --extra-dir=$(GLIB_PREFIX)/share/gtk-doc/html \ + --extra-dir=$(datadir)/gtk-doc/html + +# Used for dependencies. +HFILE_GLOB=$(top_srcdir)/gst/validate/gst-validate-scenario.h +CFILE_GLOB=$(top_srcdir)/gst/validate/gst-validate-scenario.c + +# Extra options to pass to gtkdoc-scanobj or gtkdoc-scangobj. +SCANOBJ_OPTIONS=--type-init-func="gst_init(&argc,&argv); gst_validate_init()" + +# Header files to ignore when scanning. +IGNORE_HFILES = \ + gettext.h \ + gst-validate-internal.h \ + gst-validate-monitor.h \ + gst-validate-bin-monitor.h \ + gst-validate-element-monitor.h \ + gst-validate-pad-monitor.h \ + gst-validate-override.h \ + gst-validate-override-registry.h \ + gst-validate-utils.h \ + gst-validate-media-info.h \ + gst-validate-report.h \ + media-descriptor.h \ + media-descriptor-parser.h \ + media-descriptor-writer.h \ + gst-validate-i18n-lib.h + +IGNORE_CFILES = \ + gst-validate-monitor.c \ + gst-validate-bin-monitor.c \ + gst-validate-pad-monitor.c \ + gst-validate-element-monitor.c \ + gst-validate-override.c \ + gst-validate-override-registry.c \ + gst-validate-utils.c \ + gst-validate-report.c \ + gst-validate-media-info.c \ + media-descriptor.c \ + media-descriptor-parser.c \ + media-descriptor-writer.c \ + gst-validate-i18n-lib.c + +# Images to copy into HTML directory. +# HTML_IMAGES = gdp-header.png + +# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE). +content_files = gst-validate.xml gst-validate-transcoding.xml gst-validate-media-check.xml gst-validate-launcher.xml envvariables.xml scenarios.xml + +# Other files to distribute. +extra_files = + +# CFLAGS and LDFLAGS for compiling scan program. Only needed if your app/lib +# contains GtkObjects/GObjects and you want to document signals and properties. +GTKDOC_CFLAGS = -I$(top_srcdir) $(GST_PBUTILS_CFLAGS) $(GST_BASE_CFLAGS) \ + $(GST_CFLAGS) $(GIO_CFLAGS) $(GCOV_CFLAGS) +GTKDOC_LIBS = \ + $(top_builddir)/gst/validate/libgstvalidate-@GST_API_VERSION@.la \ + $(GST_BASE_LIBS) $(GST_LIBS) $(GIO_LIBS) $(GCOV_LIBS) + +GTKDOC_CC=$(LIBTOOL) --tag=CC --mode=compile $(CC) +GTKDOC_LD=$(LIBTOOL) --tag=CC --mode=link $(CC) + +# If you need to override some of the declarations, place them in this file +# and uncomment this line. +DOC_OVERRIDES = $(DOC_MODULE)-overrides.txt + +include $(top_srcdir)/common/gtk-doc.mak + diff --git a/validate/docs/validate/envvariables.xml b/validate/docs/validate/envvariables.xml new file mode 100644 index 0000000..7a04a0e --- /dev/null +++ b/validate/docs/validate/envvariables.xml @@ -0,0 +1,188 @@ + + +%version-entities; +]> + + GstValidate Environment Variables + + + The runtime behaviour of GstValidate applications can be influenced by a number of environment variables. + + + + <envar>GST_VALIDATE</envar> + + + This environment variable can be set to a list of debug options, + which cause GstValidate to print out different types of test result information + and consider differently the level of the reported issues. + + + fatal-criticals + Causes GstValidate to consider only critical issues as import enough to consider the test failed (default behaviour) + + + + fatal-warnings + Causes GstValidate to consider warning, and critical issues as import enough to consider the test failed + + + + fatal-issues + Causes GstValidate to consider issue, warning, and critical issues as import enough to consider the test failed + + + + print-issues + Causes GstValidate to print issue, warning and critical issues in the final reports (default behaviour) + + + + print-warnings + Causes GstValidate to only print warning and critical issues in the final reports + + + + print-criticals + Causes GstValidate to only print critical issues in the final reports + + + + + + + + <envar>GST_VALIDATE_FILE</envar> + + + Set this variable to a to a colon-separated list of paths to redirect all + GstValidate messages to this file. If left unset, debug messages will be + outputed into the standard error. + + + + You can use the special names stdout and stderr to use those output. + + + + + <envar>GST_VALIDATE_SCENARIOS_PATH</envar> + + + Set this variable to a to a colon-separated list of paths. GstValidate will + scan these paths for GstValidate scenario files. + + By default GstValidate will look for scenarios in the user data directory as + specified in the XDG standard: + .local/share/gstreamer-&GST_API_VERSION;/validate/scenarios + and the system wide user data directory: /usr/lib/gstreamer-&GST_API_VERSION;/validate/scenarios + + + + + <envar>GST_VALIDATE_OVERRIDE</envar> + + + Set this variable to a colon-separated list of dynamically linkable files that GstValidate will + scan looking for overrides. + + By default GstValidate will look for scenarios in the user data directory as + specified in the XDG standard: + .local/share/gstreamer-&GST_API_VERSION;/validate/scenarios + and the system wide user data directory: /usr/lib/gstreamer-&GST_API_VERSION;/validate/scenarios + + + + + <envar>GST_VALIDATE_SCENARIO_WAIT_MULITPLIER</envar> + + + A decimal number to set as a multiplier for the wait actions. For example if you set + GST_VALIDATE_SCENARIO_WAIT_MULITPLIER=0.5, for a wait action that has a duration of 2.0 + the waiting time will only be of 1.0 second. If set to 0, wait action will be ignored. + + + + + <envar>GST_VALIDATE_REPORTING_DETAILS</envar> + + + The reporting level can be set through the GST_VALIDATE_REPORTING_DETAILS + environment variable, as a comma-separated list of (optional) object categories / names + and levels. Omit the object category / name to set the global level. + + + Examples: + + GST_VALIDATE_REPORTING_DETAILS=synthetic,h264parse:all + GST_VALIDATE_REPORTING_DETAILS=none,h264parse::sink_0:synthetic + + + + Levels being: + + + + none + No debugging level specified or desired. Used to deactivate debugging output. + + + + + synthetic + + + + Summary of the issues found, with no details. + + + + + + subchain + + + + If set as the default level, similar issues can be reported multiple times for + different subchains. If set as the level for a particular object (my_object:subchain), + validate will report the issues where the object is the first to report an issue for + a subchain. + + + + + + monitor + + + + If set as the default level, all the + distinct issues for all the monitors will be reported. + If set as the level for a particular object, all the distinct issues for this object + will be reported. + Note that if the same issue happens twice on the same object, up until this + level that issue is only reported once. + + + + + + all + + + + All the issues will be reported, even those + that repeat themselves inside the same object. This can be very verbose if + set globally. + + + + + Setting the reporting level allows to control the way issues are reported + when calling gst_validate_runner_printf(). + + + + diff --git a/validate/docs/validate/gst-validate-docs.sgml b/validate/docs/validate/gst-validate-docs.sgml new file mode 100644 index 0000000..907ac3e --- /dev/null +++ b/validate/docs/validate/gst-validate-docs.sgml @@ -0,0 +1,83 @@ + + +%version-entities; +]> + + + + GstValidate Reference Manual + + for GstValidate &GST_API_VERSION; + + + + + Overview and usage + + + GstValidate is a tool that allows GStreamer developers to check + that the GstElements they write behave the way they are supposed to. + It was first started to provide plug-ins developers with a tool to + check that they use the framework the proper way. + + + + GstValidate implements a monitoring logic that allows the system to check + that the elements of a GstPipeline respect some rules GStreamer components + have to follow to make them properly interact together. + For example, a GstValidatePadMonitor will make sure that if we receive a GstSegment + from upstream, an equivalent segment is sent downstream before any buffer gets out. + + + Then GstValidate implements a reporting system that allows users to + get detailed informations about what was not properly handled by the elements. + The generated reports are ordered by level of importance from "issue" to "critical". + + + Some tools have been implemented to help developers validate and test + their GstElement, you can have a look at the command line tools section + to find more information. + + + On top of that, the notion of a validation + scenario has been implemented so that developers can easily execute a set + of actions on pipelines to test real world interactive cases and reproduce existing + issues in a convenient way. + + + + + + + + + + + + + + API Documentation + + + + + + + + + Object Hierarchy + + + + API Index + + + + Index of deprecated API + + + + + diff --git a/validate/docs/validate/gst-validate-launcher.xml b/validate/docs/validate/gst-validate-launcher.xml new file mode 100644 index 0000000..b2818ff --- /dev/null +++ b/validate/docs/validate/gst-validate-launcher.xml @@ -0,0 +1,189 @@ + + +%version-entities; +]> + + + + The GstValidate team + see http://cgit.freedesktop.org/gstreamer/gst-devtools/ + + gst-validate + + + + gst-validate-launcher + 1 + GstValidate + &GST_API_VERSION; + GstValidate Manual Pages + + + + gst-validate-launcher + Tool to launch GstValidate testsuites + + + + + gst-validate-launcher + options + TESTSUITE + + + + + Description + gst-validate-launcher is an application to create + full testsuites on top of the GstValidate tools, testing behaviour with + dynamic pipelines and user actions (seeking, changing the pipeline + state, etc.) as described by the scenario format. + + + Run the GstValidate default testsuite + + GstValidate comes with a default testsuite to be executed on a default set of media samples. + Those media samples are stored with git-annex so you + will need it to be able to launch the default testsuite. + + + The first time you launch the testsuite, you will need to make sure that the media samples are + downloaded. To do so and launch the testsuite you can simply do: + + + gst-validate-launcher validate --sync + + + This will only launch the GstValidate tests and not other applications that might be supported + (currently ges-launch is also supported and has its own default testsuite). + + + Launching the default testsuite will open/close many windows, you might want to mute it + so you can keep using your computer: + + + gst-validate-launcher validate --sync --mute + + + + Example of a testsuite implementation + + To implement a testsuite, you will have to write some simple python code that defines + the tests to be launched by gst-validate-launcher. + + + In this example, we will assume that you want to write a whole new testsuite based on + your own media samples and scenarios. + The set of media files and the testsuite implementation file will be structured as follow: + + +testsuite_folder/ + |-> testsuite.py + |-> sample_files/ + |-> file.mp4 + |-> file1.mkv + |-> file2.ogv + |-> scenarios + |-> scenario.scenario + |-> scenario1.scenario + + + You should generate the .media_info files. To generate them for local files, + you can use: + + + gst-validate-launcher --medias-paths /path/to/sample_files/ --generate-media-info + + + For remote streams, you should use gst-validate-media-check-&GST_API_VERSION;. For an http stream you can for example do: + + + gst-validate-media-check-&GST_API_VERSION; http://someonlinestream.com/thestream \ + --output-file /path/to/testsuite_folder/sample_files/thestream.stream_info + + + The gst-validate-launcher will use the generated + .media_info and .stream_info + files to validate the tests as those contain the necessary information. + + + Then you will need to write the testsuite.py file. You can for example implement the following testsuite: + + + +import os + +# Make sure gst-validate-launcher uses our media files +options.paths = os.path.dirname(os.path.realpath(__file__)) + +# Make sure GstValidate is able to use our scenarios +# from the testsuite_folder/scenarios folder +os.environ["GST_VALIDATE_SCENARIOS_PATH"] = \ + os.path.join(os.path.dirname(os.path.realpath(__file__)), "scenarios") + +# You can activate the following if you only care about critical issues in +# the report: +# os.environ["GST_VALIDATE"] = "print_criticals" + +# Make gst-validate use our scenarios +validate.add_scenarios(["scenario", "scenario1"]) + + +# Now add "Theora and Vorbis in OGG container" as a wanted transcoding format. That means +# that conversion to this format will be tested on all the media files/streams. +validate.add_encoding_formats([MediaFormatCombination("ogg", "vorbis", "theora")]) + +# Use the GstValidatePlaybinTestsGenerator to generate tests that will use playbin +# and GstValidateTranscodingTestsGenerator to create media transcoding tests that +# will use all the media format added with validate.add_encoding_formats +validate.add_generators([validate.GstValidatePlaybinTestsGenerator(validate), + GstValidateTranscodingTestsGenerator(self)]) + +# Blacklist some tests that are known to fail because a feature is not supported +# or due to any other reason. +# The tuple defining those tests is of the form: +# ("regex defining the test name", "Reason why the test should be disabled") +validate.set_default_blacklist([ + ("validate.*.scenario1.*ogv$" + "oggdemux does not support some action executed in scenario1")] + ) + + + + + Once this is done, you've got a testsuite that will: + + + + + Run playbin pipelines on file.mp4, file1.mkv and + file2.ogv> executing scenario and scenario1 scenarios + + + + + Transcode file.mp4, file1.mkv and file2.ogv + to Theora and Vorbis in a OGG container + + + + + The only thing to do to run the testsuite is: + + + gst-validate-launcher --config /path/to/testsuite_folder/testsuite.py + + + + + Invocation + + You can find detailed information about the launcher by launching it: + + + gst-validate-launcher --help + + + diff --git a/validate/docs/validate/gst-validate-media-check.xml b/validate/docs/validate/gst-validate-media-check.xml new file mode 100644 index 0000000..ce1631b --- /dev/null +++ b/validate/docs/validate/gst-validate-media-check.xml @@ -0,0 +1,89 @@ + + +%version-entities; +]> + + + + The GstValidate team + see http://cgit.freedesktop.org/gstreamer/gst-devtools/ + + gst-validate + + + + gst-validate-media-check + 1 + GstValidate + &GST_API_VERSION; + GstValidate Manual Pages + + + + gst-validate-media-check + Tool to test GStreamer media types discovery + + + + + gst-validate-media-check + options + URI + + + + + Description + + gst-validate-media-check is command line tool checking that media files discovering works + properly with gst-discoverer over multiple runs. It needs a reference text file containing + valid information about a media file (which can be generated with the same tool) and then it will be able to check + that the reference matches what will be reported by gst-discoverer in the following runs. + + + For example, given that we have a valid reference.media_info file, we can run: + + + gst-validate-media-check-&GST_API_VERSION; file:///./file.ogv --expected-results reference.media_info + + + It will then output any error encountered and return an exit code different from 0 if any error is found. + + + + Invocation + + gst-validate-media-check takes an URI to analyze + and some extra options to control the output. + + + Options + + + + , + + The output file to store the results. + + + + + , + + Fully analize the file frame by frame. + + + + + , + + Path to file containing the expected results (or the last results found) for comparison with new results. + + + + + + + diff --git a/validate/docs/validate/gst-validate-sections.txt b/validate/docs/validate/gst-validate-sections.txt new file mode 100644 index 0000000..4abe09b --- /dev/null +++ b/validate/docs/validate/gst-validate-sections.txt @@ -0,0 +1,92 @@ +
+validate +Initialization +gst_validate_init +
+ +
+gst-validate-monitor-factory +GstValidate monitoring system +gst_validate_monitor_factory_create +
+ +
+gst-validate-runner +GstValidateRunner +GstValidateRunner +GstValidateRunnerClass +gst_validate_runner_new +gst_validate_runner_get_reports_count +gst_validate_runner_printf + +gst_validate_runner_get_reports +gst_validate_runner_add_report + +GST_IS_VALIDATE_RUNNER +GST_VALIDATE_RUNNER_CAST +GST_VALIDATE_RUNNER_CLASS_CAST +GST_IS_VALIDATE_RUNNER_CLASS +GST_TYPE_VALIDATE_RUNNER +GST_VALIDATE_RUNNER +GST_VALIDATE_RUNNER_CLASS +GST_VALIDATE_RUNNER_GET_CLASS +gst_validate_runner_get_type +
+ +
+gst-validate-scenario +GstValidateScenario +GstValidateExecuteAction +GstValidateAction +GstValidateActionParameter +GstValidateScenario +GstValidateScenarioClass +gst_validate_print_action_types +gst_validate_list_scenarios +gst_validate_register_action_type +gst_validate_action_get_clocktime +gst_validate_scenario_execute_seek +GstValidateActionType + +gst_validate_scenario_factory_create + +GST_IS_VALIDATE_ACTION +GST_IS_VALIDATE_ACTION_TYPE +GST_IS_VALIDATE_SCENARIO +GST_IS_VALIDATE_SCENARIO_CLASS +GST_TYPE_VALIDATE_ACTION +GST_TYPE_VALIDATE_ACTION_TYPE +GST_TYPE_VALIDATE_SCENARIO +GST_VALIDATE_ACTION_TYPE +GST_VALIDATE_SCENARIO +GST_VALIDATE_SCENARIO_CLASS +GST_VALIDATE_SCENARIO_GET_CLASS +GstValidateScenarioPrivate +gst_validate_action_get_type +gst_validate_action_type_get_type +gst_validate_scenario_get_type +
+ +
+gst-validate-report +GstValidate reporting system +GstValidateIssue +gst_validate_issue_new +GstValidateReport +
+ +
+gst-validate-reporter +GstValidateReporter +GstValidateReporter +GST_VALIDATE_REPORT +gst_validate_reporter_set_handle_g_logs +gst_validate_report +gst_validate_reporter_get_reports_count + +GST_TYPE_VALIDATE_REPORTER +GST_VALIDATE_REPORTER +GST_IS_VALIDATE_REPORTER +GST_VALIDATE_REPORTER_GET_INTERFACE +GST_VALIDATE_REPORTER_CAST +
diff --git a/validate/docs/validate/gst-validate-transcoding.xml b/validate/docs/validate/gst-validate-transcoding.xml new file mode 100644 index 0000000..4b69b99 --- /dev/null +++ b/validate/docs/validate/gst-validate-transcoding.xml @@ -0,0 +1,220 @@ + + +%version-entities; +]> + + + + The GstValidate team + see http://cgit.freedesktop.org/gstreamer/gst-devtools/ + + gst-validate + + + + gst-validate-transcoding + 1 + GstValidate + &GST_API_VERSION; + GstValidate Manual Pages + + + + gst-validate-transcoding + Tool to test GStreamer transcoding components + + + + + gst-validate-transcoding + options + INPUT-URI + OUTPUT-URI + + + + + Description + gst-validate-transcoding is tool to create media + files transcoding pipelines running inside the GstValidate monitoring + infrastructure. + + You can for example transcode any media file to Vorbis audio + VP8 video in a WebM container by doing: + + + gst-validate-transcoding-&GST_API_VERSION; file:///./file.ogg file:///.../transcoded.webm -o 'video/webm:video/x-vp8:audio/x-vorbis' + + + + gst-validate-transcoding will list every issue encountered during the execution of the + transcoding operation in a human readable report like the one below: + + + + issue : buffer is out of the segment range Detected on theoradec0.srcpad at 0:00:00.096556426 + + Details : buffer is out of segment and shouldn't be pushed. Timestamp: 0:00:25.000 - duration: 0:00:00.040 Range: 0:00:00.000 - 0:00:04.520 + Description : buffer being pushed is out of the current segment's start-stop range. Meaning it is going to be discarded downstream without any use + + + The return code of the process will be 18 in case a CRITICAL issue has been found. + + + + The encoding profile serialization format + This is the serialization format of a GstEncodingProfile. + + Internally the transcoding application uses GstEncodeBin. gst-validate-transcoding-&GST_API_VERSION; uses its own + serialization format to describe the GstEncodeBin.profile + property of the encodebin. + + + + The simplest serialized profile looks like: + + + muxer_source_caps:videoencoder_source_caps:audioencoder_source_caps + + + + For example to encode a stream into a WebM container, with an OGG audio stream and a VP8 video stream, + the serialized GstEncodingProfile will look like: + + + video/webm:video/x-vp8:audio/x-vorbis + + + + You can also set the preset name of the encoding profile using the caps+preset_name syntax as in: + + + video/webm:video/x-vp8+youtube-preset:audio/x-vorbis + + + + Moreover, you can set the presence property of an + encoding profile using the |presence syntax as in: + + + video/webm:video/x-vp8|1:audio/x-vorbis + + + + This field allows you to specify how many times maximum a GstEncodingProfile can be used inside an encodebin. + + + You can also use the restriction_caps->encoded_format_caps syntax to specify the + restriction caps + to be set on a GstEncodingProfile. It corresponds to the + restriction GstCaps to apply before + the encoder that will be used in the profile. The fields present in restriction + caps are properties of the raw stream (that is, before encoding), such as height + and width for video and depth and sampling rate for audio. This property does not + make sense for muxers. + + + To force a video stream to be encoded with a Full HD resolution (using WebM as the container format, + VP8 as the video codec and Vorbis as the audio codec), you should use: + + + video/webm:video/x-raw-yuv,width=1920,height=1080-->video/x-vp8:audio/x-vorbis + + + Some serialized encoding formats examples: + + MP3 audio and H264 in MP4: + + + video/quicktime,variant=iso:video/x-h264:audio/mpeg,mpegversion=1,layer=3 + + + + Vorbis and theora in OGG: + + + application/ogg:video/x-theora:audio/x-vorbis + + + + AC3 and H264 in MPEG-TS: + + + video/mpegts:video/x-h264:audio/x-ac3 + + + + + + Invocation + + gst-validate-transcoding takes and input URI and an output URI, + plus a few options to control how transcoding should be tested. + + + Options + + + + + + Let you set a scenario, it can be a full path to a scenario file + or the name of the scenario (name of the file without the + .scenario extension). + + + + + , + + List the avalaible scenarios that can be run. + + + + + + + The output file to store scenarios details. Implies . + + + + + , + + Inspect the avalaible action types with which to write scenarios + if no parameter passed, it will list all avalaible action types + otherwize will print the full description of the wanted types. + + + + + + + Let you set a config scenario. The scenario needs to be set as + config. You can specify a list of scenarios + separated by :. It will override the + GST_VALIDATE_SCENARIO environment variable. + + + + + , + + If an EOS event should be sent to the pipeline if an interrupt is + received, instead of forcing the pipeline to stop. Sending an EOS + will allow the transcoding to finish the files properly before exiting. + + + + + , + + Whether to try to force reencoding, meaning trying to only remux if possible, defaults to TRUE. + + + + + + + diff --git a/validate/docs/validate/gst-validate.types b/validate/docs/validate/gst-validate.types new file mode 100644 index 0000000..2e91b3f --- /dev/null +++ b/validate/docs/validate/gst-validate.types @@ -0,0 +1,10 @@ +#include +#include +#include + +gst_validate_action_get_type +gst_validate_action_type_get_type +gst_validate_report_get_type +gst_validate_reporter_get_type +gst_validate_runner_get_type +gst_validate_scenario_get_type diff --git a/validate/docs/validate/gst-validate.xml b/validate/docs/validate/gst-validate.xml new file mode 100644 index 0000000..7965791 --- /dev/null +++ b/validate/docs/validate/gst-validate.xml @@ -0,0 +1,139 @@ + + +%version-entities; +]> + + + + The GstValidate team + see http://cgit.freedesktop.org/gstreamer/gst-devtools/ + + gst-validate + + + + gst-validate + 1 + GstValidate + &GST_API_VERSION; + GstValidate Manual Pages + + + + gst-validate + Tool to test GStreamer components + + + + + gst-validate + options + PIPELINE-DESCRIPTION + + + + + Description + + gst-validate is the simplest gst-launch-like pipeline launcher + running inside GstValidate monitoring infrastructure. Monitors are added to it to identify issues in the + used elements. At the end it will print a report with some information + about all the issues encountered during its run. To view issues as they are detected, set the environment + variable GST_DEBUG=validate:2 and they will get printed in the GStreamer debug log. + You can basically run any GstPipeline pipeline using this tool. + If you are not familiar with gst-launch syntax, please refer to + gst-launch's documentation. + + + Simple playback pipeline: + + + gst-validate-1.0 playbin uri=file:///path/to/some/media/file + + + Transcoding pipeline: + + + gst-validate-1.0 filesrc location=/media/file/location ! qtdemux name=d ! queue \ + ! x264enc ! h264parse ! mpegtsmux name=m ! progressreport \ + ! filesink location=/root/test.ts d. ! queue ! faac ! m. + + + It will list each issue that has been encountered during the execution of the specified pipeline in a human readable report like: + + + issue : buffer is out of the segment range Detected on theoradec0.srcpad at 0:00:00.096556426 + +Details : buffer is out of segment and shouldn't be pushed. Timestamp: 0:00:25.000 - duration: 0:00:00.040 Range: 0:00:00.000 - 0:00:04.520 +Description : buffer being pushed is out of the current segment's start-stop range. Meaning it is going to be discarded downstream without any use + + + The return code of the process will be 18 in case a CRITICAL issue has been found. + + + + Invocation + + gst-validate takes a mandatory description of the + pipeline to launch, similar to gst-launch, and + some extra options. + + + Options + + + + + + Let you set a scenario, it can be a full path to a scenario file + or the name of the scenario (name of the file without the + .scenario extension). + + + + + , + + List the avalaible scenarios that can be run. + + + + + + + The output file to store scenarios details. Implies . + + + + + , + + Inspect the avalaible action types with which to write scenarios + if no parameter passed, it will list all avalaible action types + otherwize will print the full description of the wanted types. + + + + + + + Set a media_info XML file descriptor to share information about the media file that will be reproduced. + + + + + + + Let you set a config scenario. The scenario needs to be set as + config. You can specify a list of scenarios + separated by ":". It will override the + GST_VALIDATE_SCENARIO environment variable. + + + + + + + diff --git a/validate/docs/validate/scenarios.xml b/validate/docs/validate/scenarios.xml new file mode 100644 index 0000000..d1ef39c --- /dev/null +++ b/validate/docs/validate/scenarios.xml @@ -0,0 +1,143 @@ + + +%version-entities; +]> + + + GstValidate Scenario File Format + + + To be able to define a list of actions to execute on a GstPipeline, + a dedicated file format is used. + The name of the scenario is the name of the file without its .scenario extension. + The scenario file format is based on the GstStructure + serialized format which is a basic, type aware, key value format. + It takes the type of the action in the first comma separated field, and then + some key value pairs in the form parameter=value separated by commas. The values + type will be guessed if not casted as in parameter=(string)value. You can force the type + guessing system to actually know what type you want by giving it the right hints. For example + to make sure the value is a double, you should add a decimal (ie. 1 will be considered as a + int, but 1.0 will be considered as a double and "1.0" + will be considered as a string). + + + For example to represent a seek action, you should add the following line in the .scenario + file. + + + + + seek, playback-time=10.0, start=0.0, flags=accurate+flush + + + + + The files to be used as scenario should have a .scenario extension and + should be placed either in $USER_DATA_DIR/gstreamer-1.0/validate/scenarios , + $GST_DATADIR/gstreamer-1.0/validate/scenarios or in a path defined in the + $GST_VALIDATE_SCENARIOS_PATH environment variable. + + + Each line in the .scenario file represent an action (you can also use \ at the end of a line + write a single action on multiple lines). Usually you should start you scenario with a description + "config" action in order for the user to have more information about the scenario. It can contain + a summary field which is a string explaining what the scenario does and then several info fields + about the scenario. You can find more info about it running: + + + + + gst-validate-1.0 --inspect-action-type action_type_name + + + + + So a basic scenario file that will seek three times and stop would look like: + + + + + description, summary="Seeks at 1.0 to 2.0 then at \ + 3.0 to 0.0 and then seeks at \ + 1.0 to 2.0 for 1.0 second (between 2.0 and 3.0).", \ + seek=true, duration=5.0, min-media-duration=4.0 + seek, playback-time=1.0, rate=1.0, start=2.0, flags=accurate+flush + seek, playback-time=3.0, rate=1.0, start=0.0, flags=accurate+flush + seek, playback-time=1.0, rate=1.0, start=2.0, stop=3.0, flags=accurate+flush + + + + + Many action types have been implemented to help users define their own scenarios. + For example there are: + Action type examples: + + + seek: Seeks into the stream. + + + + + play: Set the pipeline state to + GST_STATE_PLAYING. + + + + + pause: Set the pipeline state to + GST_STATE_PAUSED. + + + + + stop: Stop the execution of the pipeline. + + + NOTE: This action actually posts a + GST_MESSAGE_REQUEST_STATE message requesting + GST_STATE_NULL on the bus and + the application should quit. + + + + ... + + + + + To get all the details about the registered action types, you can list them all with: + + + + + gst-validate-1.0 --inspect-action-type + + + + + and to include transcoding specific action types: + + + + + gst-validate-transcoding-1.0 --inspect-action-type + + + + + Many scenarios are distributed with gst-validate, you can list them all using: + + + + + gst-validate-1.0 --list-scenarios + + + + + You can find more information about the scenario implementation and action types in the + GstValidateScenario section. + + diff --git a/validate/docs/version.entities.in b/validate/docs/version.entities.in new file mode 100644 index 0000000..286989f --- /dev/null +++ b/validate/docs/version.entities.in @@ -0,0 +1,2 @@ + + diff --git a/validate/gst-libs/Makefile.am b/validate/gst-libs/Makefile.am new file mode 100644 index 0000000..062cb55 --- /dev/null +++ b/validate/gst-libs/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = gst diff --git a/validate/gst-libs/gst/Makefile.am b/validate/gst-libs/gst/Makefile.am new file mode 100644 index 0000000..aeb34bf --- /dev/null +++ b/validate/gst-libs/gst/Makefile.am @@ -0,0 +1,3 @@ +if HAVE_CAIRO +SUBDIRS = video +endif diff --git a/validate/gst-libs/gst/video/Makefile.am b/validate/gst-libs/gst/video/Makefile.am new file mode 100644 index 0000000..89ec269 --- /dev/null +++ b/validate/gst-libs/gst/video/Makefile.am @@ -0,0 +1,12 @@ +libgstvalidatevideo_@GST_API_VERSION@_la_SOURCES = gstvalidatessim.c gssim.c +libgstvalidatevideo_@GST_API_VERSION@include_HEADERS = gstvalidatessim.h gssim.h + +libgstvalidatevideo_@GST_API_VERSION@_la_CFLAGS = $(GST_ALL_CFLAGS) $(CAIRO_CFLAGS) $(GST_VIDEO_CFLAGS) -I$(top_builddir) +libgstvalidatevideo_@GST_API_VERSION@_la_LIBADD = $(GST_ALL_LIBS) $(CAIRO_LIBS) $(GST_VIDEO_LIBS) $(top_builddir)/gst/validate/libgstvalidate-@GST_API_VERSION@.la +libgstvalidatevideo_@GST_API_VERSION@_la_LDFLAGS = $(GST_ALL_LDFLAGS) $(CAIRO_LDFLAGS) $(GST_VIDEO_LDFLAGS) + +lib_LTLIBRARIES = libgstvalidatevideo-@GST_API_VERSION@.la + +libgstvalidatevideo_@GST_API_VERSION@includedir = $(includedir)/gstreamer-@GST_API_VERSION@/lib/validate/video + +CLEANFILES = diff --git a/validate/gst-libs/gst/video/gssim.c b/validate/gst-libs/gst/video/gssim.c new file mode 100644 index 0000000..60acce5 --- /dev/null +++ b/validate/gst-libs/gst/video/gssim.c @@ -0,0 +1,453 @@ +/* GStreamer + * + * Copyright (C) 2014 Mathieu Duponchelle + * Copyright (C) 2015 Raspberry Pi Foundation + * Author: Thibault Saunier + * + * This library 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.1 of the License, or (at your option) any later version. + * + * This library 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; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#include +#include +#include +#include + +#include "gssim.h" + +typedef gfloat (*SSimWeightFunc) (Gssim * self, gint y, gint x); + +typedef struct _SSimWindowCache +{ + gint x_window_start; + gint x_weight_start; + gint x_window_end; + gint y_window_start; + gint y_weight_start; + gint y_window_end; + gfloat element_summ; +} SSimWindowCache; + +/* *INDENT-OFF* */ +G_DEFINE_TYPE (Gssim, gssim, G_TYPE_OBJECT) +/* *INDENT-ON* */ + +enum +{ + PROP_FIRST_PROP = 1, + N_PROPS +}; + +struct _GssimPriv +{ + gint width; + gint height; + gint windowsize; + gint windowtype; + SSimWindowCache *windows; + gfloat *weights; + gfloat const1; + gfloat const2; + gfloat sigma; + + gfloat *orgmu; + + GstVideoConverter *converter; + GstVideoInfo in_info, out_info; +}; + + +static void +gssim_calculate_mu (Gssim * self, guint8 * buf) +{ + gint oy, ox, iy, ix; + + for (oy = 0; oy < self->priv->height; oy++) { + for (ox = 0; ox < self->priv->width; ox++) { + gfloat mu = 0; + gfloat elsumm; + gint weight_y_base, weight_x_base; + gint weight_offset; + gint pixel_offset; + gint winstart_y; + gint wghstart_y; + gint winend_y; + gint winstart_x; + gint wghstart_x; + gint winend_x; + gfloat weight; + gint source_offset; + + source_offset = oy * self->priv->width + ox; + + winstart_x = self->priv->windows[source_offset].x_window_start; + wghstart_x = self->priv->windows[source_offset].x_weight_start; + winend_x = self->priv->windows[source_offset].x_window_end; + winstart_y = self->priv->windows[source_offset].y_window_start; + wghstart_y = self->priv->windows[source_offset].y_weight_start; + winend_y = self->priv->windows[source_offset].y_window_end; + elsumm = self->priv->windows[source_offset].element_summ; + + switch (self->priv->windowtype) { + case 0: + for (iy = winstart_y; iy <= winend_y; iy++) { + pixel_offset = iy * self->priv->width; + for (ix = winstart_x; ix <= winend_x; ix++) + mu += buf[pixel_offset + ix]; + } + mu = mu / elsumm; + break; + case 1: + + weight_y_base = wghstart_y - winstart_y; + weight_x_base = wghstart_x - winstart_x; + + for (iy = winstart_y; iy <= winend_y; iy++) { + pixel_offset = iy * self->priv->width; + weight_offset = (weight_y_base + iy) * self->priv->windowsize + + weight_x_base; + for (ix = winstart_x; ix <= winend_x; ix++) { + weight = self->priv->weights[weight_offset + ix]; + mu += weight * buf[pixel_offset + ix]; + } + } + mu = mu / elsumm; + break; + } + self->priv->orgmu[oy * self->priv->width + ox] = mu; + } + } +} + +static gfloat +ssim_weight_func_none (Gssim * self, gint y, gint x) +{ + return 1; +} + +static gfloat +ssim_weight_func_gauss (Gssim * self, gint y, gint x) +{ + gfloat coord = sqrt (x * x + y * y); + return exp (-1 * (coord * coord) / (2 * self->priv->sigma * + self->priv->sigma)) / (self->priv->sigma * sqrt (2 * G_PI)); +} + + +static gboolean +gssim_regenerate_windows (Gssim * self) +{ + gint windowiseven; + gint y, x, y2, x2; + SSimWeightFunc func; + gfloat normal_summ = 0; + gint normal_count = 0; + + g_free (self->priv->weights); + + self->priv->weights = + g_new (gfloat, self->priv->windowsize * self->priv->windowsize); + + windowiseven = + ((gint) self->priv->windowsize / 2) * 2 == self->priv->windowsize ? 1 : 0; + + g_free (self->priv->windows); + + self->priv->windows = + g_new (SSimWindowCache, self->priv->height * self->priv->width); + + switch (self->priv->windowtype) { + case 0: + func = ssim_weight_func_none; + break; + case 1: + func = ssim_weight_func_gauss; + break; + default: + self->priv->windowtype = 1; + func = ssim_weight_func_gauss; + } + + for (y = 0; y < self->priv->windowsize; y++) { + gint yoffset = y * self->priv->windowsize; + for (x = 0; x < self->priv->windowsize; x++) { + self->priv->weights[yoffset + x] = + func (self, x - self->priv->windowsize / 2 + windowiseven, + y - self->priv->windowsize / 2 + windowiseven); + normal_summ += self->priv->weights[yoffset + x]; + normal_count++; + } + } + + for (y = 0; y < self->priv->height; y++) { + for (x = 0; x < self->priv->width; x++) { + SSimWindowCache win; + gint element_count = 0; + + win.x_window_start = x - self->priv->windowsize / 2 + windowiseven; + win.x_weight_start = 0; + if (win.x_window_start < 0) { + win.x_weight_start = -win.x_window_start; + win.x_window_start = 0; + } + + win.x_window_end = x + self->priv->windowsize / 2; + if (win.x_window_end >= self->priv->width) + win.x_window_end = self->priv->width - 1; + + win.y_window_start = y - self->priv->windowsize / 2 + windowiseven; + win.y_weight_start = 0; + if (win.y_window_start < 0) { + win.y_weight_start = -win.y_window_start; + win.y_window_start = 0; + } + + win.y_window_end = y + self->priv->windowsize / 2; + if (win.y_window_end >= self->priv->height) + win.y_window_end = self->priv->height - 1; + + win.element_summ = 0; + element_count = (win.y_window_end - win.y_window_start + 1) * + (win.x_window_end - win.x_window_start + 1); + if (element_count == normal_count) + win.element_summ = normal_summ; + else { + for (y2 = win.y_weight_start; y2 < self->priv->windowsize; y2++) { + for (x2 = win.x_weight_start; x2 < self->priv->windowsize; x2++) { + win.element_summ += + self->priv->weights[y2 * self->priv->windowsize + x2]; + } + } + } + self->priv->windows[(y * self->priv->width + x)] = win; + } + } + + /* FIXME: while 0.01 and 0.03 are pretty much static, the 255 implies that + * we're working with 8-bit-per-color-component format, which may not be true + */ + self->priv->const1 = 0.01 * 255 * 0.01 * 255; + self->priv->const2 = 0.03 * 255 * 0.03 * 255; + return TRUE; +} + +void +gssim_compare (Gssim * self, guint8 * org, guint8 * mod, + guint8 * out, gfloat * mean, gfloat * lowest, gfloat * highest) +{ + gint oy, ox, iy, ix; + gfloat cumulative_ssim = 0; + *lowest = G_MAXFLOAT; + *highest = -G_MAXFLOAT; + + if (self->priv->windows == NULL) + gssim_regenerate_windows (self); + gssim_calculate_mu (self, org); + + for (oy = 0; oy < self->priv->height; oy++) { + for (ox = 0; ox < self->priv->width; ox++) { + gfloat mu_o = 0, mu_m = 0; + gdouble sigma_o = 0, sigma_m = 0, sigma_om = 0; + gfloat tmp1, tmp2; + gfloat elsumm = 0; + gint weight_y_base, weight_x_base; + gint weight_offset; + gint pixel_offset; + gint winstart_y; + gint wghstart_y; + gint winend_y; + gint winstart_x; + gint wghstart_x; + gint winend_x; + gfloat weight; + gint source_offset; + + source_offset = oy * self->priv->width + ox; + + winstart_x = self->priv->windows[source_offset].x_window_start; + wghstart_x = self->priv->windows[source_offset].x_weight_start; + winend_x = self->priv->windows[source_offset].x_window_end; + winstart_y = self->priv->windows[source_offset].y_window_start; + wghstart_y = self->priv->windows[source_offset].y_weight_start; + winend_y = self->priv->windows[source_offset].y_window_end; + elsumm = self->priv->windows[source_offset].element_summ; + + switch (self->priv->windowtype) { + case 0: + for (iy = winstart_y; iy <= winend_y; iy++) { + pixel_offset = iy * self->priv->width; + for (ix = winstart_x; ix <= winend_x; ix++) { + mu_m += mod[pixel_offset + ix]; + } + } + mu_m = mu_m / elsumm; + mu_o = self->priv->orgmu[oy * self->priv->width + ox]; + for (iy = winstart_y; iy <= winend_y; iy++) { + pixel_offset = iy * self->priv->width; + for (ix = winstart_x; ix <= winend_x; ix++) { + tmp1 = org[pixel_offset + ix] - mu_o; + tmp2 = mod[pixel_offset + ix] - mu_m; + sigma_o += tmp1 * tmp1; + sigma_m += tmp2 * tmp2; + sigma_om += tmp1 * tmp2; + } + } + break; + case 1: + + weight_y_base = wghstart_y - winstart_y; + weight_x_base = wghstart_x - winstart_x; + + for (iy = winstart_y; iy <= winend_y; iy++) { + pixel_offset = iy * self->priv->width; + weight_offset = (weight_y_base + iy) * self->priv->windowsize + + weight_x_base; + for (ix = winstart_x; ix <= winend_x; ix++) { + weight = self->priv->weights[weight_offset + ix]; + mu_o += weight * org[pixel_offset + ix]; + mu_m += weight * mod[pixel_offset + ix]; + } + } + mu_m = mu_m / elsumm; + mu_o = self->priv->orgmu[oy * self->priv->width + ox]; + for (iy = winstart_y; iy <= winend_y; iy++) { + gfloat *weights_with_offset; + guint8 *org_with_offset, *mod_with_offset; + gfloat wt1, wt2; + pixel_offset = iy * self->priv->width; + weight_offset = (weight_y_base + iy) * self->priv->windowsize + + weight_x_base; + weights_with_offset = &self->priv->weights[weight_offset]; + org_with_offset = &org[pixel_offset]; + mod_with_offset = &mod[pixel_offset]; + for (ix = winstart_x; ix <= winend_x; ix++) { + weight = weights_with_offset[ix]; + tmp1 = org_with_offset[ix] - mu_o; + tmp2 = mod_with_offset[ix] - mu_m; + wt1 = weight * tmp1; + wt2 = weight * tmp2; + sigma_o += wt1 * tmp1; + sigma_m += wt2 * tmp2; + sigma_om += wt1 * tmp2; + } + } + break; + } + sigma_o = sqrt (sigma_o / elsumm); + sigma_m = sqrt (sigma_m / elsumm); + sigma_om = sigma_om / elsumm; + tmp1 = + (2 * mu_o * mu_m + self->priv->const1) * (2 * sigma_om + + self->priv->const2) / ((mu_o * mu_o + mu_m * mu_m + + self->priv->const1) * (sigma_o * sigma_o + sigma_m * sigma_m + + self->priv->const2)); + + /* SSIM can go negative, that's why it is + 127 + index * 128 instead of index * 255 */ + if (out) + out[oy * self->priv->width + ox] = 127 + tmp1 * 128; + *lowest = MIN (*lowest, tmp1); + *highest = MAX (*highest, tmp1); + cumulative_ssim += tmp1; + } + } + *mean = cumulative_ssim / (self->priv->width * self->priv->height); +} + +gboolean +gssim_configure (Gssim * self, gint width, gint height) +{ + if (width == self->priv->width && height == self->priv->height) + return FALSE; + + self->priv->width = width; + self->priv->height = height; + + g_free (self->priv->windows); + self->priv->windows = NULL; + if (self->priv->orgmu) + g_free (self->priv->orgmu); + + self->priv->orgmu = g_new (gfloat, width * height); + + return TRUE; +} + +static void +gssim_get_property (GObject * object, + guint property_id, GValue * value, GParamSpec * pspec) +{ + //Gssim *self = GSSIM (object); + + switch (property_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gssim_set_property (GObject * object, + guint property_id, const GValue * value, GParamSpec * pspec) +{ + //Gssim *self = GSSIM (object); + + switch (property_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gssim_finalize (GObject * object) +{ + Gssim *self = GSSIM (object); + void (*chain_up) (GObject *) = + ((GObjectClass *) gssim_parent_class)->finalize; + + g_free (self->priv->orgmu); + g_free (self->priv->windows); + + chain_up (object); +} + +static void +gssim_class_init (GssimClass * klass) +{ + GObjectClass *oclass = G_OBJECT_CLASS (klass); + + oclass->get_property = gssim_get_property; + oclass->set_property = gssim_set_property; + oclass->finalize = gssim_finalize; + + g_type_class_add_private (klass, sizeof (GssimPriv)); +} + +static void +gssim_init (Gssim * self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GSSIM_TYPE, GssimPriv); + + self->priv->windowsize = 11; + self->priv->windowtype = 1; + self->priv->windows = NULL; + self->priv->sigma = 1.5; +} + +Gssim * +gssim_new (void) +{ + return g_object_new (GSSIM_TYPE, NULL); +} diff --git a/validate/gst-libs/gst/video/gssim.h b/validate/gst-libs/gst/video/gssim.h new file mode 100644 index 0000000..e78313b --- /dev/null +++ b/validate/gst-libs/gst/video/gssim.h @@ -0,0 +1,62 @@ +/* GStreamer + * + * Copyright (C) 2014 Mathieu Duponchelle + * Copyright (C) 2015 Raspberry Pi Foundation + * Author: Thibault Saunier + * + * This library 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.1 of the License, or (at your option) any later version. + * + * This library 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; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _GSSIM_H +#define _GSSIM_H + +#include +#include +#include +#include + +G_BEGIN_DECLS + +typedef struct _GssimPriv GssimPriv; + +typedef struct { + GObject parent; + + GssimPriv *priv; +} Gssim; + +typedef struct { + GObjectClass parent; +} GssimClass; + +#define GSSIM_TYPE (gssim_get_type ()) +#define GSSIM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSSIM_TYPE, Gssim)) +#define GSSIM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSSIM_TYPE, GssimClass)) +#define IS_GSSIM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSSIM_TYPE)) +#define IS_GSSIM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSSIM_TYPE)) +#define GSSIM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSSIM_TYPE, GssimClass)) + +GType gssim_get_type (void); +Gssim * gssim_new (void); + +void gssim_compare (Gssim * self, guint8 * org, guint8 * mod, + guint8 * out, gfloat * mean, gfloat * lowest, + gfloat * highest); +gboolean gssim_configure (Gssim * self, gint width, gint height); + +G_END_DECLS + +#endif diff --git a/validate/gst-libs/gst/video/gstvalidatessim.c b/validate/gst-libs/gst/video/gstvalidatessim.c new file mode 100644 index 0000000..8bbdc95 --- /dev/null +++ b/validate/gst-libs/gst/video/gstvalidatessim.c @@ -0,0 +1,972 @@ +/* GStreamer + * + * Copyright (C) 2015 Raspberry Pi Foundation + * Author: Thibault Saunier + * + * This library 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.1 of the License, or (at your option) any later version. + * + * This library 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; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#include +#include +#include +#include +#include + +#include +#include "gstvalidatessim.h" +#include "gssim.h" + +#include +#include +#include +#include + +#define GST_CAT_DEFAULT gstvalidatessim_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +#define SIMILARITY_ISSUE_WITH_PREVIOUS g_quark_from_static_string ("ssim::image-not-similar-enough-with-theoretical-reference") +#define SIMILARITY_ISSUE g_quark_from_static_string ("ssim::image-not-similar-enough") +#define GENERAL_INPUT_ERROR g_quark_from_static_string ("ssim::general-file-error") +#define WRONG_FORMAT g_quark_from_static_string ("ssim::wrong-format") + +G_DEFINE_TYPE_WITH_CODE (GstValidateSsim, gst_validate_ssim, + G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (GST_TYPE_VALIDATE_REPORTER, NULL)); + +enum +{ + PROP_FIRST_PROP = 1, + PROP_RUNNER, + PROP_LAST +}; + +typedef struct +{ + GstVideoConverter *converter; + GstVideoInfo in_info; + GstVideoInfo out_info; +} SSimConverterInfo; + +static void +ssim_convert_info_free (SSimConverterInfo * info) +{ + if (info->converter) + gst_video_converter_free (info->converter); + + g_slice_free (SSimConverterInfo, info); +} + +struct _GstValidateSsimPriv +{ + gint width; + gint height; + + Gssim *ssim; + + GList *converters; + GstVideoInfo out_info; + + SSimConverterInfo outconverter_info; + + gfloat min_avg_similarity; + gfloat min_lowest_similarity; + + GHashTable *ref_frames_cache; +}; + + +static gboolean +gst_validate_ssim_convert (GstValidateSsim * self, SSimConverterInfo * info, + GstVideoFrame * frame, GstVideoFrame * converted_frame) +{ + gboolean res = TRUE; + GstBuffer *outbuf = NULL; + + g_return_val_if_fail (info != NULL, FALSE); + + outbuf = gst_buffer_new_allocate (NULL, info->out_info.size, NULL); + if (!gst_video_frame_map (converted_frame, &info->out_info, outbuf, + GST_MAP_WRITE)) { + GST_VALIDATE_REPORT (self, GENERAL_INPUT_ERROR, + "Could not map output converted_frame"); + goto fail; + } + + gst_video_converter_frame (info->converter, frame, converted_frame); + +done: + if (outbuf) + gst_buffer_unref (outbuf); + + return res; + +fail: + res = FALSE; + goto done; +} + +static void +gst_validate_ssim_save_out (GstValidateSsim * self, GstBuffer * buffer, + const gchar * ref_file, const gchar * file, const gchar * outfolder) +{ + GstVideoFrame frame, converted; + + if (!g_file_test (outfolder, G_FILE_TEST_IS_DIR)) { + if (g_mkdir_with_parents (outfolder, 0755) != 0) { + + GST_VALIDATE_REPORT (self, GENERAL_INPUT_ERROR, + "Could not create output directory %s", outfolder); + return; + } + } + + if (self->priv->outconverter_info.converter == NULL || + self->priv->width != self->priv->outconverter_info.out_info.width || + self->priv->height != self->priv->outconverter_info.out_info.height) { + + if (self->priv->outconverter_info.converter) + gst_video_converter_free (self->priv->outconverter_info.converter); + + gst_video_info_init (&self->priv->outconverter_info.in_info); + gst_video_info_set_format (&self->priv->outconverter_info.in_info, + GST_VIDEO_FORMAT_GRAY8, self->priv->width, self->priv->height); + + gst_video_info_init (&self->priv->outconverter_info.out_info); + gst_video_info_set_format (&self->priv->outconverter_info.out_info, + GST_VIDEO_FORMAT_RGBx, self->priv->width, self->priv->height); + + self->priv->outconverter_info.converter = + gst_video_converter_new (&self->priv->outconverter_info.in_info, + &self->priv->outconverter_info.out_info, NULL); + } + + if (!gst_video_frame_map (&frame, &self->priv->outconverter_info.in_info, + buffer, GST_MAP_READ)) { + GST_VALIDATE_REPORT (self, GENERAL_INPUT_ERROR, + "Could not map output frame"); + + return; + } + + if (gst_validate_ssim_convert (self, &self->priv->outconverter_info, + &frame, &converted)) { + cairo_status_t status; + cairo_surface_t *surface; + gchar *bn1 = g_path_get_basename (ref_file); + gchar *bn2 = g_path_get_basename (file); + gchar *fname = g_strdup_printf ("%s.VS.%s.result.png", bn1, bn2); + gchar *outfile = g_build_path (G_DIR_SEPARATOR_S, outfolder, fname, NULL); + + surface = + cairo_image_surface_create_for_data (GST_VIDEO_FRAME_PLANE_DATA + (&converted, 0), CAIRO_FORMAT_RGB24, GST_VIDEO_FRAME_WIDTH (&converted), + GST_VIDEO_FRAME_HEIGHT (&converted), + GST_VIDEO_FRAME_PLANE_STRIDE (&converted, 0)); + + if ((status = cairo_surface_write_to_png (surface, outfile)) != + CAIRO_STATUS_SUCCESS) { + GST_VALIDATE_REPORT (self, GENERAL_INPUT_ERROR, + "Could not save %s" " cairo status is %s", outfile, + cairo_status_to_string (status)); + } + + cairo_surface_destroy (surface); + gst_video_frame_unmap (&frame); + gst_video_frame_unmap (&converted); + g_free (bn1); + g_free (bn2); + g_free (fname); + g_free (outfile); + } +} + +static gboolean +gst_validate_ssim_configure (GstValidateSsim * self, gint width, gint height) +{ + if (width == self->priv->width && height == self->priv->height) + return FALSE; + + gssim_configure (self->priv->ssim, width, height); + + self->priv->width = width; + self->priv->height = height; + + gst_video_info_init (&self->priv->out_info); + gst_video_info_set_format (&self->priv->out_info, GST_VIDEO_FORMAT_I420, + self->priv->width, self->priv->height); + + return TRUE; +} + +static void +gst_validate_ssim_configure_converter (GstValidateSsim * self, gint index, + gboolean force, GstVideoFormat in_format, gint width, gint height) +{ + SSimConverterInfo *info = g_list_nth_data (self->priv->converters, index); + + if (!info) { + info = g_slice_new0 (SSimConverterInfo); + + self->priv->converters = + g_list_insert (self->priv->converters, info, index); + } + + if (force || info->in_info.height != height || info->in_info.width != width || + info->in_info.finfo->format != in_format) { + gst_video_info_init (&info->in_info); + gst_video_info_set_format (&info->in_info, in_format, width, height); + + if (info->converter) + gst_video_converter_free (info->converter); + + info->out_info = self->priv->out_info; + + if (gst_video_info_is_equal (&info->in_info, &info->out_info)) + info->converter = NULL; + else + info->converter = + gst_video_converter_new (&info->in_info, &info->out_info, NULL); + } +} + +static GstVideoFormat +_get_format_from_surface (cairo_surface_t * surface) +{ +#if G_BYTE_ORDER == G_BIG_ENDIAN + if (cairo_surface_get_content (surface) == CAIRO_CONTENT_COLOR_ALPHA) + return GST_VIDEO_FORMAT_BGRA; + else + return GST_VIDEO_FORMAT_BGRx; +#else + if (cairo_surface_get_content (surface) == CAIRO_CONTENT_COLOR_ALPHA) + return GST_VIDEO_FORMAT_ARGB; + else + return GST_VIDEO_FORMAT_RGBx; +#endif +} + +void +gst_validate_ssim_compare_frames (GstValidateSsim * self, + GstVideoFrame * ref_frame, GstVideoFrame * frame, GstBuffer ** outbuf, + gfloat * mean, gfloat * lowest, gfloat * highest) +{ + gboolean reconf; + guint8 *outdata = NULL; + GstMapInfo map1, map2, outmap; + + GstVideoFrame converted_frame1, converted_frame2; + SSimConverterInfo *convinfo1, *convinfo2; + + reconf = + gst_validate_ssim_configure (self, ref_frame->info.width, + ref_frame->info.height); + + gst_validate_ssim_configure_converter (self, 0, reconf, + ref_frame->info.finfo->format, ref_frame->info.width, + ref_frame->info.height); + + gst_validate_ssim_configure_converter (self, 1, reconf, + frame->info.finfo->format, frame->info.width, frame->info.height); + + convinfo1 = (SSimConverterInfo *) g_list_nth_data (self->priv->converters, 0); + if (convinfo1->converter) + gst_validate_ssim_convert (self, convinfo1, ref_frame, &converted_frame1); + else + converted_frame1 = *ref_frame; + + convinfo2 = (SSimConverterInfo *) g_list_nth_data (self->priv->converters, 0); + if (convinfo2->converter) + gst_validate_ssim_convert (self, convinfo2, frame, &converted_frame2); + else + converted_frame2 = *frame; + + if (!gst_buffer_map (converted_frame1.buffer, &map1, GST_MAP_READ)) { + GST_VALIDATE_REPORT (self, GENERAL_INPUT_ERROR, + "Could not map reference frame"); + + return; + } + + if (!gst_buffer_map (converted_frame2.buffer, &map2, GST_MAP_READ)) { + gst_buffer_unmap (converted_frame1.buffer, &map1); + GST_VALIDATE_REPORT (self, GENERAL_INPUT_ERROR, + "Could not map compared frame"); + + return; + } + + if (outbuf) { + *outbuf = gst_buffer_new_and_alloc (GST_ROUND_UP_4 (self->priv->width) * + self->priv->height); + if (!gst_buffer_map (*outbuf, &outmap, GST_MAP_WRITE)) { + GST_VALIDATE_REPORT (self, GENERAL_INPUT_ERROR, + "Could not map output frame"); + + gst_buffer_unref (*outbuf); + gst_buffer_unmap (converted_frame1.buffer, &map1); + gst_buffer_unmap (converted_frame2.buffer, &map2); + *outbuf = NULL; + + return; + } + + outdata = outmap.data; + } + + gssim_compare (self->priv->ssim, map1.data, map2.data, outdata, mean, + lowest, highest); + + gst_buffer_unmap (ref_frame->buffer, &map1); + gst_buffer_unmap (frame->buffer, &map2); + + if (convinfo1->converter) + gst_video_frame_unmap (&converted_frame1); + if (convinfo2->converter) + gst_video_frame_unmap (&converted_frame2); + + if (outbuf) + gst_buffer_unmap (*outbuf, &outmap); +} + +static gboolean +gst_validate_ssim_get_frame_from_png (GstValidateSsim * self, const char *file, + GstVideoFrame * frame) +{ + guint8 *data; + GstBuffer *buf; + GstVideoInfo info; + cairo_surface_t *surface = NULL; + + surface = cairo_image_surface_create_from_png (file); + if (surface == NULL + || (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS)) { + GST_VALIDATE_REPORT (self, GENERAL_INPUT_ERROR, "Could not open %s: %s", + file, cairo_status_to_string (cairo_surface_status (surface))); + + return FALSE; + } + + gst_video_info_init (&info); + gst_video_info_set_format (&info, + _get_format_from_surface (surface), + cairo_image_surface_get_width (surface), + cairo_image_surface_get_height (surface)); + + data = cairo_image_surface_get_data (surface); + buf = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, + data, info.size, 0, info.size, surface, + (GDestroyNotify) cairo_surface_destroy); + if (!gst_video_frame_map (frame, &info, buf, GST_MAP_READ)) { + gst_buffer_unref (buf); + GST_VALIDATE_REPORT (self, GENERAL_INPUT_ERROR, + "Could not map input frame"); + + return FALSE; + } + + gst_buffer_unref (buf); + + return TRUE; +} + +static gboolean +gst_validate_ssim_get_frame_from_file (GstValidateSsim * self, const char *file, + GstVideoFrame * frame) +{ + gchar *data; + gsize length; + GstBuffer *buf; + GstVideoInfo info; + GstVideoFormat format; + gint strv_length, width, height; + + gboolean res = TRUE; + gchar **splited_name = NULL, **splited_size = NULL, *strformat; + + GError *error = NULL; + + if (g_str_has_suffix (file, ".png")) { + return gst_validate_ssim_get_frame_from_png (self, file, frame); + } + + splited_name = g_strsplit (file, ".", -1); + strv_length = g_strv_length (splited_name); + + strformat = splited_name[strv_length - 1]; + format = gst_video_format_from_string (strformat); + if (format == GST_VIDEO_FORMAT_UNKNOWN) { + GST_VALIDATE_REPORT (self, WRONG_FORMAT, "Unknown format: %s", strformat); + + goto fail; + } + + splited_size = g_strsplit (splited_name[strv_length - 2], "x", -1); + if (g_strv_length (splited_size) != 2) { + GST_VALIDATE_REPORT (self, WRONG_FORMAT, + "Can not determine video size from filename: %s ", file); + + goto fail; + } + + errno = 0; + width = g_ascii_strtoull (splited_size[0], NULL, 10); + if (errno) { + GST_VALIDATE_REPORT (self, WRONG_FORMAT, + "Can not determine video size from filename: %s ", file); + + goto fail; + } + + errno = 0; + height = g_ascii_strtoull (splited_size[1], NULL, 10); + if (errno) { + GST_VALIDATE_REPORT (self, WRONG_FORMAT, + "Can not determine video size from filename: %s ", file); + + goto fail; + } + + gst_video_info_init (&info); + gst_video_info_set_format (&info, format, width, height); + + if (!g_file_get_contents (file, &data, &length, &error)) { + GST_VALIDATE_REPORT (self, GENERAL_INPUT_ERROR, "Could not open %s: %s", + file, error->message); + g_error_free (error); + + goto fail; + } + + buf = gst_buffer_new_wrapped (data, length); + if (!gst_video_frame_map (frame, &info, buf, GST_MAP_READ)) { + gst_buffer_unref (buf); + GST_VALIDATE_REPORT (self, GENERAL_INPUT_ERROR, + "Could not map input frame"); + + goto fail; + } + gst_buffer_unref (buf); + +done: + g_strfreev (splited_name); + g_strfreev (splited_size); + + return res; + +fail: + res = FALSE; + + goto done; +} + +static gboolean +_filename_get_timestamp (GstValidateSsim * self, const gchar * filename, + GstClockTime * ts) +{ + guint h, m, s, ns; + gchar *other = g_strdup (filename); + + if (sscanf (filename, "%" GST_TIME_FORMAT "%s", &h, &m, &s, &ns, other) < 4) { + GST_INFO_OBJECT (self, "Can not sscanf %s", filename); + g_free (other); + + return FALSE; + } + + g_free (other); + *ts = (h * 3600 + m * 60 + s) * GST_SECOND + ns; + + return TRUE; +} + +typedef struct +{ + gchar *path; + GstClockTime ts; +} Frame; + +static void +_free_frame (Frame * frame) +{ + g_free (frame->path); +} + +static gint +_sort_frames (Frame * a, Frame * b) +{ + if (a->ts < b->ts) + return -1; + + if (a->ts == b->ts) + return 0; + + return 1; +} + +static Frame * +_find_frame (GstValidateSsim * self, GArray * frames, GstClockTime ts, + gboolean get_next) +{ + guint i; + Frame *lframe = &g_array_index (frames, Frame, 0); + + for (i = 1; i < frames->len; i++) { + Frame *iframe = &g_array_index (frames, Frame, i); + + if (ts >= lframe->ts && iframe->ts > ts) { + if (get_next) + return iframe; + + return lframe; + } else if (i + 1 == frames->len) { + return iframe; + } + + lframe = iframe; + } + + return NULL; +} + +static GArray * +_get_ref_frame_cache (GstValidateSsim * self, const gchar * ref_file) +{ + GFile *ref_dir_file = NULL; + GFileInfo *info; + GFileEnumerator *fenum; + GArray *frames = NULL; + gchar *ref_dir = NULL; + + ref_dir = g_path_get_dirname (ref_file); + + frames = g_hash_table_lookup (self->priv->ref_frames_cache, ref_file); + if (frames) + goto done; + + ref_dir_file = g_file_new_for_path (ref_dir); + if (!(fenum = g_file_enumerate_children (ref_dir_file, + "standard::*", G_FILE_QUERY_INFO_NONE, NULL, NULL))) { + GST_INFO ("%s is not a folder", ref_dir); + + goto done; + } + + for (info = g_file_enumerator_next_file (fenum, NULL, NULL); + info; info = g_file_enumerator_next_file (fenum, NULL, NULL)) { + Frame iframe; + const gchar *display_name = g_file_info_get_display_name (info); + + if (!_filename_get_timestamp (self, display_name, &iframe.ts)) { + g_object_unref (info); + continue; + } + + iframe.path = g_build_path (G_DIR_SEPARATOR_S, + ref_dir, g_file_info_get_name (info), NULL); + + g_object_unref (info); + + if (!frames) { + frames = g_array_new (TRUE, TRUE, sizeof (Frame)); + + g_array_set_clear_func (frames, (GDestroyNotify) _free_frame); + } + g_array_append_val (frames, iframe); + } + + if (frames) { + g_array_sort (frames, (GCompareFunc) _sort_frames); + + g_hash_table_insert (self->priv->ref_frames_cache, g_strdup (ref_dir), + frames); + } + +done: + g_clear_object (&ref_dir_file); + g_free (ref_dir); + + return frames; +} + +static gchar * +_get_ref_file_path (GstValidateSsim * self, const gchar * ref_file, + const gchar * file, gboolean get_next) +{ + Frame *frame; + GArray *frames; + gchar *real_ref_file = NULL, *fbname = NULL; + GstClockTime file_ts; + + if (!g_strrstr (ref_file, "*")) + return g_strdup (ref_file); + + fbname = g_path_get_basename (file); + if (!_filename_get_timestamp (self, fbname, &file_ts)) { + + goto done; + } + + frames = _get_ref_frame_cache (self, ref_file); + if (frames) { + frame = _find_frame (self, frames, file_ts, get_next); + + if (frame) + real_ref_file = g_strdup (frame->path); + } + +done: + g_free (fbname); + + return real_ref_file; +} + +static gboolean +gst_validate_ssim_compare_image_file (GstValidateSsim * self, + const gchar * ref_file, const gchar * file, gfloat * mean, gfloat * lowest, + gfloat * highest, const gchar * outfolder) +{ + GstBuffer *outbuf = NULL, **poutbuf = NULL; + gboolean res = TRUE; + GstVideoFrame ref_frame, frame; + gchar *real_ref_file = NULL; + + real_ref_file = _get_ref_file_path (self, ref_file, file, FALSE); + + if (!real_ref_file) { + GST_VALIDATE_REPORT (self, GENERAL_INPUT_ERROR, + "Could find ref file for %s", ref_file); + goto fail; + } + + if (!gst_validate_ssim_get_frame_from_file (self, real_ref_file, &ref_frame)) + goto fail; + + + if (!gst_validate_ssim_get_frame_from_file (self, file, &frame)) { + gst_video_frame_unmap (&ref_frame); + + goto fail; + } + + if (outfolder) { + poutbuf = &outbuf; + } + + gst_validate_ssim_compare_frames (self, &ref_frame, &frame, + poutbuf, mean, lowest, highest); + + if (*mean < self->priv->min_avg_similarity) { + gst_video_frame_unmap (&ref_frame); + gst_video_frame_unmap (&frame); + + if (g_strcmp0 (ref_file, real_ref_file)) { + gchar *tmpref = real_ref_file; + + real_ref_file = _get_ref_file_path (self, ref_file, file, TRUE); + + GST_VALIDATE_REPORT (self, SIMILARITY_ISSUE_WITH_PREVIOUS, + "\nComparing %s with %s failed, (mean %f " + " min %f), checking next %s\n", tmpref, file, + *mean, *lowest, real_ref_file); + + g_free (tmpref); + + res = gst_validate_ssim_compare_image_file (self, + real_ref_file, file, mean, lowest, highest, outfolder); + goto done; + } + + GST_VALIDATE_REPORT (self, SIMILARITY_ISSUE, + "Average similarity '%f' between %s and %s inferior" + " than the minimum average: %f", *mean, + real_ref_file, file, self->priv->min_avg_similarity); + + goto fail; + } + + if (*lowest < self->priv->min_lowest_similarity) { + GST_VALIDATE_REPORT (self, SIMILARITY_ISSUE, + "Lowest similarity '%f' between %s and %s inferior" + " than the minimum lowest similarity: %f", *lowest, + real_ref_file, file, self->priv->min_lowest_similarity); + + gst_video_frame_unmap (&ref_frame); + gst_video_frame_unmap (&frame); + + goto fail; + } + + gst_video_frame_unmap (&ref_frame); + gst_video_frame_unmap (&frame); + +done: + + g_free (real_ref_file); + if (outbuf) + gst_buffer_unref (outbuf); + + return res; + +fail: + res = FALSE; + + if (outbuf) + gst_validate_ssim_save_out (self, outbuf, real_ref_file, file, outfolder); + + goto done; +} + +static gboolean +_check_directory (GstValidateSsim * self, const gchar * ref_dir, + const gchar * compared_dir, gfloat * mean, gfloat * lowest, + gfloat * highest, const gchar * outfolder) +{ + gint nfiles = 0, nnotfound = 0, nfailures = 0; + gboolean res = TRUE; + GFileInfo *info; + GFileEnumerator *fenum; + gfloat min_avg = 1.0, min_min = 1.0, total_avg = 0; + GFile *file = g_file_new_for_path (ref_dir); + + if (!(fenum = g_file_enumerate_children (file, + "standard::*", G_FILE_QUERY_INFO_NONE, NULL, NULL))) { + GST_INFO ("%s is not a folder", ref_dir); + res = FALSE; + + goto done; + } + + for (info = g_file_enumerator_next_file (fenum, NULL, NULL); + info; info = g_file_enumerator_next_file (fenum, NULL, NULL)) { + + if (g_file_info_get_file_type (info) == G_FILE_TYPE_REGULAR || + g_file_info_get_file_type (info) == G_FILE_TYPE_SYMBOLIC_LINK) { + gchar *compared_file = g_build_path (G_DIR_SEPARATOR_S, + compared_dir, g_file_info_get_name (info), NULL); + gchar *ref_file = NULL; + + if (!g_file_test (compared_file, G_FILE_TEST_IS_REGULAR)) { + GST_INFO_OBJECT (self, "Could not find file %s", compared_file); + nnotfound++; + res = FALSE; + } else { + + ref_file = + g_build_path (G_DIR_SEPARATOR_S, ref_dir, + g_file_info_get_name (info), NULL); + if (!gst_validate_ssim_compare_image_files (self, ref_file, + compared_file, mean, lowest, highest, outfolder)) { + nfailures++; + res = FALSE; + } else { + nfiles++; + } + } + + min_avg = MIN (min_avg, *mean); + min_min = MIN (min_min, *lowest); + total_avg += *mean; + gst_validate_printf (NULL, + "\r", + g_file_info_get_display_name (info), + GST_TIME_ARGS (GST_CLOCK_TIME_NONE), + *mean, *lowest, nfiles, nfailures, nnotfound); + + g_free (compared_file); + g_free (ref_file); + } + + g_object_unref (info); + } + + gst_validate_printf (NULL, + "\nAverage similarity: %f, min_avg: %f, min_min: %f\n", + total_avg / nfiles, min_avg, min_min); + +done: + gst_object_unref (file); + if (fenum) + gst_object_unref (fenum); + + return res; +} + +gboolean +gst_validate_ssim_compare_image_files (GstValidateSsim * self, + const gchar * ref_file, const gchar * file, gfloat * mean, gfloat * lowest, + gfloat * highest, const gchar * outfolder) +{ + if (g_file_test (ref_file, G_FILE_TEST_IS_DIR)) { + if (!g_file_test (file, G_FILE_TEST_IS_DIR)) { + GST_VALIDATE_REPORT (self, GENERAL_INPUT_ERROR, + "%s is a directory but %s is not", ref_file, file); + + return FALSE; + } + + return _check_directory (self, ref_file, file, mean, lowest, highest, + outfolder); + } else { + return gst_validate_ssim_compare_image_file (self, ref_file, file, mean, + lowest, highest, outfolder); + } +} + +static void +gst_validate_ssim_get_property (GObject * object, + guint property_id, GValue * value, GParamSpec * pspec) +{ + switch (property_id) { + case PROP_RUNNER: + /* we assume the runner is valid as long as this scenario is, + * no ref taken */ + g_value_set_object (value, + gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER (object))); + break; + default: + break; + } +} + +static void +gst_validate_ssim_set_property (GObject * object, + guint property_id, const GValue * value, GParamSpec * pspec) +{ + switch (property_id) { + case PROP_RUNNER: + /* we assume the runner is valid as long as this scenario is, + * no ref taken */ + gst_validate_reporter_set_runner (GST_VALIDATE_REPORTER (object), + g_value_get_object (value)); + break; + default: + break; + } +} + +static void +gst_validate_ssim_dispose (GObject * object) +{ + GstValidateSsim *self = GST_VALIDATE_SSIM (object); + void (*chain_up) (GObject *) = + ((GObjectClass *) gst_validate_ssim_parent_class)->dispose; + + gst_object_unref (self->priv->ssim); + + chain_up (object); +} + +static void +gst_validate_ssim_finalize (GObject * object) +{ + GstValidateSsim *self = GST_VALIDATE_SSIM (object); + void (*chain_up) (GObject *) = + ((GObjectClass *) gst_validate_ssim_parent_class)->finalize; + + g_list_free_full (self->priv->converters, + (GDestroyNotify) ssim_convert_info_free); + + if (self->priv->outconverter_info.converter) + gst_video_converter_free (self->priv->outconverter_info.converter); + g_hash_table_unref (self->priv->ref_frames_cache); + + chain_up (object); +} + +static gpointer +_register_issues (gpointer data) +{ + gst_validate_issue_register (gst_validate_issue_new (SIMILARITY_ISSUE, + "Compared images where not similar enough", + "The images checker detected that the images" + " it is comparing did not have the similarity" + " level as defined with min-avg-similarity or" + " min-lowest-similarity", GST_VALIDATE_REPORT_LEVEL_CRITICAL)); + + gst_validate_issue_register (gst_validate_issue_new + (SIMILARITY_ISSUE_WITH_PREVIOUS, + "Comparison with theoretically reference image failed", + " In a case were we have reference frames with the following" + " timestamps: [0.00, 0.10, 0.20, 0.30] comparing a frame with" + " 0.05 as a timestamp will be done with the first frame. " + " If that fails, it will report a ssim::image-not-similar-enough-with-theoretical-reference" + " warning and try with the second reference frame.", + GST_VALIDATE_REPORT_LEVEL_WARNING)); + + gst_validate_issue_register (gst_validate_issue_new (GENERAL_INPUT_ERROR, + "Something went wrong handling image files", + "An error accured when working with input files", + GST_VALIDATE_REPORT_LEVEL_CRITICAL)); + + gst_validate_issue_register (gst_validate_issue_new (WRONG_FORMAT, + "The format or dimensions of the compared images do not match ", + "The format or dimensions of the compared images do not match ", + GST_VALIDATE_REPORT_LEVEL_CRITICAL)); + + return NULL; +} + +static void +gst_validate_ssim_class_init (GstValidateSsimClass * klass) +{ + GObjectClass *oclass = G_OBJECT_CLASS (klass); + static GOnce _once = G_ONCE_INIT; + + GST_DEBUG_CATEGORY_INIT (gstvalidatessim_debug, "validatessim", 0, + "Validate ssim plugin"); + + oclass->get_property = gst_validate_ssim_get_property; + oclass->set_property = gst_validate_ssim_set_property; + oclass->dispose = gst_validate_ssim_dispose; + oclass->finalize = gst_validate_ssim_finalize; + + g_type_class_add_private (klass, sizeof (GstValidateSsimPriv)); + + g_once (&_once, _register_issues, NULL); + + g_object_class_install_property (oclass, PROP_RUNNER, + g_param_spec_object ("validate-runner", "VALIDATE Runner", + "The Validate runner to " "report errors to", + GST_TYPE_VALIDATE_RUNNER, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); +} + +static void +gst_validate_ssim_init (GstValidateSsim * self) +{ + self->priv = + G_TYPE_INSTANCE_GET_PRIVATE (self, GST_VALIDATE_SSIM_TYPE, + GstValidateSsimPriv); + + self->priv->ssim = gssim_new (); + self->priv->ref_frames_cache = g_hash_table_new_full (g_str_hash, + g_str_equal, g_free, (GDestroyNotify) g_array_unref); +} + +GstValidateSsim * +gst_validate_ssim_new (GstValidateRunner * runner, + gfloat min_avg_similarity, gfloat min_lowest_similarity) +{ + GstValidateSsim *self = + g_object_new (GST_VALIDATE_SSIM_TYPE, "validate-runner", runner, NULL); + + self->priv->min_avg_similarity = min_avg_similarity; + self->priv->min_lowest_similarity = min_lowest_similarity; + + gst_validate_reporter_set_name (GST_VALIDATE_REPORTER (self), + g_strdup ("gst-validate-images-checker")); + + return self; +} diff --git a/validate/gst-libs/gst/video/gstvalidatessim.h b/validate/gst-libs/gst/video/gstvalidatessim.h new file mode 100644 index 0000000..484a3d1 --- /dev/null +++ b/validate/gst-libs/gst/video/gstvalidatessim.h @@ -0,0 +1,73 @@ +/* GStreamer + * + * Copyright (C) 2014 Mathieu Duponchelle + * Copyright (C) 2015 Raspberry Pi Foundation + * Author: Thibault Saunier + * + * This library 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.1 of the License, or (at your option) any later version. + * + * This library 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; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _GST_VALIDATE_SSIM_H +#define _GST_VALIDATE_SSIM_H + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include + +G_BEGIN_DECLS + +typedef struct _GstValidateSsimPriv GstValidateSsimPriv; + +typedef struct { + GObject parent; + + GstValidateSsimPriv *priv; +} GstValidateSsim; + +typedef struct { + GObjectClass parent; +} GstValidateSsimClass; + +#define GST_VALIDATE_SSIM_TYPE (gst_validate_ssim_get_type ()) +#define GST_VALIDATE_SSIM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_VALIDATE_SSIM_TYPE, GstValidateSsim)) +#define GST_VALIDATE_SSIM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_VALIDATE_SSIM_TYPE, GstValidateSsimClass)) +#define IS_GST_VALIDATE_SSIM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_VALIDATE_SSIM_TYPE)) +#define IS_GST_VALIDATE_SSIM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_VALIDATE_SSIM_TYPE)) +#define GST_VALIDATE_SSIM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_VALIDATE_SSIM_TYPE, GstValidateSsimClass)) + +GType gst_validate_ssim_get_type (void); + +GstValidateSsim * gst_validate_ssim_new (GstValidateRunner *runner, + gfloat min_avg_similarity, + gfloat min_lowest_similarity); + +gboolean gst_validate_ssim_compare_image_files (GstValidateSsim *self, const gchar *ref_file, + const gchar * file, gfloat * mean, gfloat * lowest, + gfloat * highest, const gchar *outfolder); + +void gst_validate_ssim_compare_frames (GstValidateSsim * self, GstVideoFrame *ref_frame, + GstVideoFrame *frame, GstBuffer **outbuf, + gfloat * mean, gfloat * lowest, gfloat * highest); + +G_END_DECLS + +#endif diff --git a/validate/gst-validate-devel.manifest b/validate/gst-validate-devel.manifest new file mode 100644 index 0000000..a76fdba --- /dev/null +++ b/validate/gst-validate-devel.manifest @@ -0,0 +1,5 @@ + + + + + diff --git a/validate/gst-validate.doap b/validate/gst-validate.doap new file mode 100644 index 0000000..f56a483 --- /dev/null +++ b/validate/gst-validate.doap @@ -0,0 +1,81 @@ + + + GStreamer + gstreamer + + 1999-10-31 + + GstValidate is a testing framework aiming at providing GStreamer developers + tools that check the GstElements they write behave the way they are supposed to. + + + GstValidate is a tool that allows GStreamer developers to check that the + GstElements they write behave the way they are supposed to. It was first + started to provide plug-ins developers with a tool to check that they use the + framework the proper way. + + GstValidate implements a monitoring logic that allows the system to check that + the elements of a GstPipeline respect some rules GStreamer components have to + follow so that elements can properly interact together. For example, a + GstValidatePadMonitor will make sure that if we receive a GstSegment from + upstream, an equivalent segment is sent downstream before any buffer gets out. + + Then GstValidate implements a reporting system that allows users to get + detailed informations about what was not properly handle in elements. The + reports are order by level of importance from "issue" to "critical". + + Some tools have been implemented to help the developer validate and test their + GstElement, you can have a look at the command line tools section to find more + information + + On top of those tools, the notion of scenario has been implemented so that + developers can easily execute a set of actions on pipelines and thus test real + world interactive cases and reproduce existing issues in a convenient way. + + + + + + C + + + + + + + + + + + + + 1.5.2 + 1.5 + + 2014-09-29 + + + + + + + 1.4.0 + 1.4 + + 2014-09-29 + + + + + + + Thibault Saunier + + + + diff --git a/validate/gst-validate.manifest b/validate/gst-validate.manifest new file mode 100644 index 0000000..a76fdba --- /dev/null +++ b/validate/gst-validate.manifest @@ -0,0 +1,5 @@ + + + + + diff --git a/validate/gst/Makefile.am b/validate/gst/Makefile.am new file mode 100644 index 0000000..7e44971 --- /dev/null +++ b/validate/gst/Makefile.am @@ -0,0 +1,5 @@ +SUBDIRS = validate overrides + +if HAVE_LD_PRELOAD +SUBDIRS += preload +endif diff --git a/validate/gst/overrides/Makefile.am b/validate/gst/overrides/Makefile.am new file mode 100644 index 0000000..b9aba7f --- /dev/null +++ b/validate/gst/overrides/Makefile.am @@ -0,0 +1,14 @@ +lib_LTLIBRARIES = libgstvalidate-default-overrides-@GST_API_VERSION@.la +libgstvalidate_default_overrides_@GST_API_VERSION@_la_SOURCES = \ + gst-validate-default-overrides.c + +libgstvalidate_default_overrides_@GST_API_VERSION@_la_CFLAGS = $(GST_ALL_CFLAGS) $(GIO_CFLAGS) +libgstvalidate_default_overrides_@GST_API_VERSION@_la_LDFLAGS = $(GST_LIB_LDFLAGS) $(GST_ALL_LDFLAGS) \ + $(GST_LT_LDFLAGS) $(GIO_LDFLAGS) $(top_builddir)/gst/validate/libgstvalidate-1.0.la +libgstvalidate_default_overrides_@GST_API_VERSION@_la_LIBADD = \ + $(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS) \ + $(GST_ALL_LIBS) $(GIO_LIBS) +libgstvalidate_default_overrides_@GST_API_VERSION@includedir = $(includedir)/gstreamer-@GST_API_VERSION@/gst/validate +libgstvalidate_default_overrides_@GST_API_VERSION@include_HEADERS = + +CLEANFILES = diff --git a/validate/gst/overrides/gst-validate-default-overrides.c b/validate/gst/overrides/gst-validate-default-overrides.c new file mode 100644 index 0000000..9e2e96c --- /dev/null +++ b/validate/gst/overrides/gst-validate-default-overrides.c @@ -0,0 +1,47 @@ +/* GStreamer + * + * Copyright (C) 2013 Collabora Ltd. + * Author: Vincent Penquerc'h + * + * gst-validate-default-overrides.c - Test overrides + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include + +/* public symbol */ +int gst_validate_create_overrides (void); + +int +gst_validate_create_overrides (void) +{ + GstValidateOverride *o; + + /* Some random test override. Will moan on: + gst-launch videotestsrc num-buffers=10 ! video/x-raw-yuv ! fakesink */ + o = gst_validate_override_new (); + gst_validate_override_change_severity (o, + g_quark_from_string ("caps::is-missing-field"), + GST_VALIDATE_REPORT_LEVEL_CRITICAL); + gst_validate_override_register_by_name ("capsfilter0", o); + return 1; +} diff --git a/validate/gst/preload/Makefile.am b/validate/gst/preload/Makefile.am new file mode 100644 index 0000000..66faee0 --- /dev/null +++ b/validate/gst/preload/Makefile.am @@ -0,0 +1,15 @@ +libgstvalidate_preload_@GST_API_VERSION@_la_SOURCES = \ + gst-validate-monitor-preload.c + +libgstvalidate_preload_@GST_API_VERSION@_la_CFLAGS = $(GST_ALL_CFLAGS) +libgstvalidate_preload_@GST_API_VERSION@_la_LDFLAGS = $(GST_LIB_LDFLAGS) $(GST_ALL_LDFLAGS) \ + $(GST_LT_LDFLAGS) $(top_builddir)/gst/validate/libgstvalidate-1.0.la +libgstvalidate_preload_@GST_API_VERSION@_la_LIBADD = \ + $(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS) \ + $(GST_ALL_LIBS) +libgstvalidate_preload_@GST_API_VERSION@includedir = $(includedir)/gstreamer-@GST_API_VERSION@/gst/validate +libgstvalidate_preload_@GST_API_VERSION@include_HEADERS = + +lib_LTLIBRARIES = libgstvalidate_preload-@GST_API_VERSION@.la + +CLEANFILES = diff --git a/validate/gst/preload/gst-validate-monitor-preload.c b/validate/gst/preload/gst-validate-monitor-preload.c new file mode 100644 index 0000000..370a94e --- /dev/null +++ b/validate/gst/preload/gst-validate-monitor-preload.c @@ -0,0 +1,164 @@ +/* GStreamer + * + * Copyright (C) 2013 Collabora Ltd. + * Author: Thiago Sousa Santos + * + * gst-validate-monitor-preload.c - Validate Element monitors preload functions + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include + +#define __USE_GNU +#include + +static GstValidateRunner *runner = NULL; + +static void +exit_report_printer (void) +{ + if (runner) + gst_validate_runner_exit (runner, TRUE); +} + +/* + * Functions that wrap object creation so gst-validate can be used + * to monitor 'standard' applications + */ + +static void +gst_validate_preload_wrap (GstElement * element) +{ + if (runner == NULL) { + gst_validate_init (); + runner = gst_validate_runner_new (); + atexit (exit_report_printer); + } + + /* the reference to the monitor is lost */ + gst_validate_monitor_factory_create (GST_OBJECT_CAST (element), runner, NULL); +} + +GstElement * +gst_element_factory_make (const gchar * element_name, const gchar * name) +{ + static GstElement *(*gst_element_factory_make_real) (const gchar *, + const gchar *) = NULL; + GstElement *element; + + if (!gst_element_factory_make_real) + gst_element_factory_make_real = + dlsym (RTLD_NEXT, "gst_element_factory_make"); + + element = gst_element_factory_make_real (element_name, name); + + if (GST_IS_PIPELINE (element)) { + gst_validate_preload_wrap (element); + } + return element; +} + +GstElement * +gst_pipeline_new (const gchar * name) +{ + static GstElement *(*gst_pipeline_new_real) (const gchar *) = NULL; + GstElement *element; + + if (!gst_pipeline_new_real) + gst_pipeline_new_real = dlsym (RTLD_NEXT, "gst_pipeline_new"); + + element = gst_pipeline_new_real (name); + gst_validate_preload_wrap (element); + return element; +} + +GstElement * +gst_parse_launchv (const gchar ** argv, GError ** error) +{ + static GstElement *(*gst_parse_launchv_real) (const gchar **, GError **) = + NULL; + GstElement *element; + + if (!gst_parse_launchv_real) + gst_parse_launchv_real = dlsym (RTLD_NEXT, "gst_parse_launchv"); + + element = gst_parse_launchv_real (argv, error); + if (GST_IS_PIPELINE (element)) { + gst_validate_preload_wrap (element); + } + return element; +} + +GstElement * +gst_parse_launchv_full (const gchar ** argv, GstParseContext * context, + GstParseFlags flags, GError ** error) +{ + static GstElement *(*gst_parse_launchv_full_real) (const gchar **, + GstParseContext *, GstParseFlags, GError **) = NULL; + GstElement *element; + + if (!gst_parse_launchv_full_real) + gst_parse_launchv_full_real = dlsym (RTLD_NEXT, "gst_parse_launchv_full"); + + element = gst_parse_launchv_full_real (argv, context, flags, error); + if (GST_IS_PIPELINE (element)) { + gst_validate_preload_wrap (element); + } + return element; +} + +GstElement * +gst_parse_launch (const gchar * pipeline_description, GError ** error) +{ + static GstElement *(*gst_parse_launch_real) (const gchar *, GError **) = NULL; + GstElement *element; + + if (!gst_parse_launch_real) + gst_parse_launch_real = dlsym (RTLD_NEXT, "gst_parse_launch"); + + element = gst_parse_launch_real (pipeline_description, error); + if (GST_IS_PIPELINE (element)) { + gst_validate_preload_wrap (element); + } + return element; +} + +GstElement * +gst_parse_launch_full (const gchar * pipeline_description, + GstParseContext * context, GstParseFlags flags, GError ** error) +{ + static GstElement *(*gst_parse_launch_full_real) (const gchar *, + GstParseContext *, GstParseFlags, GError **) = NULL; + GstElement *element; + + if (!gst_parse_launch_full_real) + gst_parse_launch_full_real = dlsym (RTLD_NEXT, "gst_parse_launch_full"); + + element = + gst_parse_launch_full_real (pipeline_description, context, flags, error); + if (GST_IS_PIPELINE (element)) { + gst_validate_preload_wrap (element); + } + return element; +} diff --git a/validate/gst/validate/Makefile.am b/validate/gst/validate/Makefile.am new file mode 100644 index 0000000..9c50921 --- /dev/null +++ b/validate/gst/validate/Makefile.am @@ -0,0 +1,122 @@ +libgstvalidate_@GST_API_VERSION@_la_SOURCES = \ + gst-validate-runner.c \ + gst-validate-reporter.c \ + gst-validate-monitor.c \ + gst-validate-element-monitor.c \ + gst-validate-bin-monitor.c \ + gst-validate-pipeline-monitor.c \ + gst-validate-pad-monitor.c \ + gst-validate-monitor-factory.c \ + gst-validate-report.c \ + gst-validate-scenario.c \ + gst-validate-override.c \ + gst-validate-utils.c \ + gst-validate-override-registry.c \ + media-descriptor.c \ + media-descriptor-writer.c \ + media-descriptor-parser.c \ + gst-validate-media-info.c \ + validate.c + +libgstvalidate_@GST_API_VERSION@include_HEADERS = \ + validate.h \ + gst-validate-types.h \ + gst-validate-bin-monitor.h \ + gst-validate-pipeline-monitor.h \ + gst-validate-element-monitor.h \ + gst-validate-enums.h \ + media-descriptor.h \ + media-descriptor-writer.h \ + media-descriptor-parser.h \ + gst-validate-monitor-factory.h \ + gst-validate-monitor.h \ + gst-validate-override.h \ + gst-validate-override-registry.h \ + gst-validate-pad-monitor.h \ + gst-validate-reporter.h \ + gst-validate-report.h \ + gst-validate-runner.h \ + gst-validate-scenario.h \ + gst-validate-utils.h \ + gst-validate-media-info.h + +noinst_HEADERS = \ + gettext.h \ + gst-validate-i18n-lib.h \ + gst-validate-internal.h + +lib_LTLIBRARIES = libgstvalidate-@GST_API_VERSION@.la +libgstvalidate_@GST_API_VERSION@_la_CFLAGS = $(GST_ALL_CFLAGS)\ + $(GIO_CFLAGS) $(GST_PBUTILS_CFLAGS) +libgstvalidate_@GST_API_VERSION@_la_LDFLAGS = $(GST_LIB_LDFLAGS) $(GST_ALL_LDFLAGS) \ + $(GST_LT_LDFLAGS) $(GIO_LDFLAGS) $(GST_PBUTILS_LDFAGS) +libgstvalidate_@GST_API_VERSION@_la_LIBADD = \ + $(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS) \ + $(GST_ALL_LIBS) $(GIO_LIBS) $(GST_PBUTILS_LIBS) \ + $(GLIB_LIBS) $(LIBM) + +libgstvalidate_@GST_API_VERSION@includedir = $(includedir)/gstreamer-@GST_API_VERSION@/gst/validate + +CLEANFILES = + +if HAVE_INTROSPECTION +BUILT_GIRSOURCES = GstValidate-@GST_API_VERSION@.gir + +gir_headers=$(patsubst %,$(srcdir)/%, $(libgstvalidate_@GST_API_VERSION@include_HEADERS)) +gir_headers+=$(patsubst %,$(builddir)/%, $(built_header_make)) +gir_sources=$(patsubst %,$(srcdir)/%, $(libgstvalidate_@GST_API_VERSION@_la_SOURCES)) +gir_sources+=$(patsubst %,$(builddir)/%, $(built_source_make)) +gir_cincludes=$(patsubst %,--c-include='gst/validate/%',$(libgstvalidate@GST_API_VERSION@include_HEADERS)) + +GstValidate-@GST_API_VERSION@.gir: $(INTROSPECTION_SCANNER) libgstvalidate-@GST_API_VERSION@.la + $(INTROSPECTION_SCANNER) -v --namespace GstValidate \ + --nsversion=@GST_API_VERSION@ \ + --warn-all \ + $(gir_cincludes) \ + -I$(top_srcdir) \ + -I$(top_builddir) \ + --library=libgstvalidate-@GST_API_VERSION@.la \ + --include=GLib-2.0 \ + --include=GstVideo-@GST_API_VERSION@ \ + --include=GstPbutils-@GST_API_VERSION@ \ + --include=GObject-2.0 \ + --include=GModule-2.0 \ + --include=GLib-2.0 \ + --libtool="${LIBTOOL}" \ + $(FAULTINJECTION_LIBS) \ + --pkg gstreamer-@GST_API_VERSION@ \ + --pkg gstreamer-pbutils-@GST_API_VERSION@ \ + --pkg gstreamer-controller-@GST_API_VERSION@ \ + --pkg glib-2.0 \ + --pkg gobject-2.0 \ + --pkg-export gstvalidate-@GST_API_VERSION@ \ + --add-init-section="gst_init(NULL, NULL);" \ + --output $@ \ + $(gir_headers) \ + $(gir_sources) + +# INTROSPECTION_GIRDIR/INTROSPECTION_TYPELIBDIR aren't the right place to +# install anything - we need to install inside our prefix. +girdir = $(datadir)/gir-1.0 +gir_DATA = $(BUILT_GIRSOURCES) + +typelibsdir = $(libdir)/girepository-1.0/ + +typelibs_DATA = $(BUILT_GIRSOURCES:.gir=.typelib) + +%.typelib: %.gir $(INTROSPECTION_COMPILER) + $(AM_V_GEN)PKG_CONFIG_PATH="$(GST_PKG_CONFIG_PATH)" \ + $(INTROSPECTION_COMPILER) \ + --includedir=$(srcdir) \ + --includedir=$(srcdir)/../video \ + --includedir=$(builddir) \ + --includedir=`$(PKG_CONFIG) --variable=girdir gstreamer-@GST_API_VERSION@` \ + --includedir=`$(PKG_CONFIG) --variable=girdir gstreamer-pbutils-@GST_API_VERSION@` \ + --includedir=`$(PKG_CONFIG) --variable=girdir gstreamer-video-@GST_API_VERSION@` \ + --includedir=`$(PKG_CONFIG) --variable=girdir gstreamer-base-@GST_API_VERSION@` \ + --includedir=`$(PKG_CONFIG) --variable=girdir gstreamer-controller-@GST_API_VERSION@` \ + --includedir=`$(PKG_CONFIG) --variable=girdir gio-2.0` \ + $(INTROSPECTION_COMPILER_OPTS) $< -o $(@F) +endif + +CLEANFILES += $(BUILT_GIRSOURCES) $(typelibs_DATA) diff --git a/validate/gst/validate/gettext.h b/validate/gst/validate/gettext.h new file mode 100644 index 0000000..59902b3 --- /dev/null +++ b/validate/gst/validate/gettext.h @@ -0,0 +1,69 @@ +/* Convenience header for conditional use of GNU . + Copyright (C) 1995-1998, 2000-2002 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published + by the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. */ + +#ifndef _LIBGETTEXT_H +#define _LIBGETTEXT_H 1 + +/* NLS can be disabled through the configure --disable-nls option. */ +#ifdef ENABLE_NLS + +/* Get declarations of GNU message catalog functions. */ +# include + +#else + +/* Solaris /usr/include/locale.h includes /usr/include/libintl.h, which + chokes if dcgettext is defined as a macro. So include it now, to make + later inclusions of a NOP. We don't include + as well because people using "gettext.h" will not include , + and also including would fail on SunOS 4, whereas + is OK. */ +#if defined(__sun) +# include +#endif + +/* Disabled NLS. + The casts to 'const char *' serve the purpose of producing warnings + for invalid uses of the value returned from these functions. + On pre-ANSI systems without 'const', the config.h file is supposed to + contain "#define const". */ +# define gettext(Msgid) ((const char *) (Msgid)) +# define dgettext(Domainname, Msgid) ((const char *) (Msgid)) +# define dcgettext(Domainname, Msgid, Category) ((const char *) (Msgid)) +# define ngettext(Msgid1, Msgid2, N) \ + ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2)) +# define dngettext(Domainname, Msgid1, Msgid2, N) \ + ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2)) +# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \ + ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2)) +# define textdomain(Domainname) ((const char *) (Domainname)) +# define bindtextdomain(Domainname, Dirname) ((const char *) (Dirname)) +# define bind_textdomain_codeset(Domainname, Codeset) ((const char *) (Codeset)) + +#endif + +/* A pseudo function call that serves as a marker for the automated + extraction of messages, but does not call gettext(). The run-time + translation is done at a different place in the code. + The argument, String, should be a literal string. Concatenated strings + and other string expressions won't work. + The macro's expansion is not parenthesized, so that it is suitable as + initializer for static 'char[]' or 'const char[]' variables. */ +#define gettext_noop(String) String + +#endif /* _LIBGETTEXT_H */ diff --git a/validate/gst/validate/gst-validate-bin-monitor.c b/validate/gst/validate/gst-validate-bin-monitor.c new file mode 100644 index 0000000..fdb5125 --- /dev/null +++ b/validate/gst/validate/gst-validate-bin-monitor.c @@ -0,0 +1,270 @@ +/* GStreamer + * + * Copyright (C) 2013 Collabora Ltd. + * Author: Thiago Sousa Santos + * + * gst-validate-bin-monitor.c - Validate BinMonitor class + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "gst-validate-internal.h" +#include "gst-validate-bin-monitor.h" +#include "gst-validate-monitor-factory.h" + +#define PRINT_POSITION_TIMEOUT 250 + +/** + * SECTION:gst-validate-bin-monitor + * @short_description: Class that wraps a #GstBin for Validate checks + * + * TODO + */ + +enum +{ + PROP_0, + PROP_HANDLES_STATE, + PROP_LAST +}; + +#define gst_validate_bin_monitor_parent_class parent_class +G_DEFINE_TYPE (GstValidateBinMonitor, gst_validate_bin_monitor, + GST_TYPE_VALIDATE_ELEMENT_MONITOR); + +static void +gst_validate_bin_monitor_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static void +gst_validate_bin_monitor_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void +gst_validate_bin_monitor_wrap_element (GstValidateBinMonitor * monitor, + GstElement * element); +static gboolean gst_validate_bin_monitor_setup (GstValidateMonitor * monitor); + +static void +_validate_bin_element_added (GstBin * bin, GstElement * pad, + GstValidateBinMonitor * monitor); + +static void +gst_validate_bin_set_media_descriptor (GstValidateMonitor * monitor, + GstMediaDescriptor * media_descriptor) +{ + GList *tmp; + + GST_VALIDATE_MONITOR_LOCK (monitor); + for (tmp = GST_VALIDATE_BIN_MONITOR_CAST (monitor)->element_monitors; tmp; + tmp = tmp->next) + gst_validate_monitor_set_media_descriptor (tmp->data, media_descriptor); + GST_VALIDATE_MONITOR_UNLOCK (monitor); + + GST_VALIDATE_MONITOR_CLASS (parent_class)->set_media_descriptor (monitor, + media_descriptor); +} + +static void +gst_validate_bin_monitor_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + switch (prop_id) { + case PROP_HANDLES_STATE: + g_assert_not_reached (); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_validate_bin_monitor_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstValidateBinMonitor *monitor; + + monitor = GST_VALIDATE_BIN_MONITOR_CAST (object); + + switch (prop_id) { + case PROP_HANDLES_STATE: + if (monitor->scenario == NULL) + g_value_set_boolean (value, FALSE); + else + g_object_get_property (G_OBJECT (monitor->scenario), "handles-states", + value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_validate_bin_monitor_dispose (GObject * object) +{ + GstValidateBinMonitor *monitor = GST_VALIDATE_BIN_MONITOR_CAST (object); + GstElement *bin = GST_VALIDATE_ELEMENT_MONITOR_GET_ELEMENT (monitor); + + if (bin && monitor->element_added_id) + g_signal_handler_disconnect (bin, monitor->element_added_id); + + if (monitor->scenario) + g_object_unref (monitor->scenario); + + g_list_free_full (monitor->element_monitors, g_object_unref); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + + +static void +gst_validate_bin_monitor_class_init (GstValidateBinMonitorClass * klass) +{ + GObjectClass *gobject_class; + GstValidateMonitorClass *validatemonitor_class; + + gobject_class = G_OBJECT_CLASS (klass); + validatemonitor_class = GST_VALIDATE_MONITOR_CLASS_CAST (klass); + + gobject_class->get_property = gst_validate_bin_monitor_get_property; + gobject_class->set_property = gst_validate_bin_monitor_set_property; + gobject_class->dispose = gst_validate_bin_monitor_dispose; + + g_object_class_install_property (gobject_class, PROP_HANDLES_STATE, + g_param_spec_boolean ("handles-states", "Handles state", + "True if the application should not set handle the first state change " + " False if it is application responsibility", + FALSE, G_PARAM_READABLE)); + + validatemonitor_class->setup = gst_validate_bin_monitor_setup; + validatemonitor_class->set_media_descriptor = + gst_validate_bin_set_media_descriptor; +} + +static void +gst_validate_bin_monitor_init (GstValidateBinMonitor * bin_monitor) +{ +} + +/** + * gst_validate_bin_monitor_new: + * @bin: (transfer-none): a #GstBin to run Validate on + */ +GstValidateBinMonitor * +gst_validate_bin_monitor_new (GstBin * bin, GstValidateRunner * runner, + GstValidateMonitor * parent) +{ + GstValidateBinMonitor *monitor = + g_object_new (GST_TYPE_VALIDATE_BIN_MONITOR, "object", + bin, "validate-runner", runner, "validate-parent", parent, NULL); + + if (GST_VALIDATE_MONITOR_GET_OBJECT (monitor) == NULL) { + g_object_unref (monitor); + return NULL; + } + + return monitor; +} + +static gboolean +gst_validate_bin_monitor_setup (GstValidateMonitor * monitor) +{ + GstIterator *iterator; + gboolean done; + GstElement *element; + GstValidateBinMonitor *bin_monitor = GST_VALIDATE_BIN_MONITOR_CAST (monitor); + GstBin *bin = GST_VALIDATE_BIN_MONITOR_GET_BIN (bin_monitor); + + if (!GST_IS_BIN (bin)) { + GST_WARNING_OBJECT (monitor, "Trying to create bin monitor with other " + "type of object"); + return FALSE; + } + + GST_DEBUG_OBJECT (bin_monitor, "Setting up monitor for bin %" GST_PTR_FORMAT, + bin); + + if (g_object_get_data ((GObject *) bin, "validate-monitor")) { + GST_DEBUG_OBJECT (bin_monitor, + "Bin already has a validate-monitor associated"); + return FALSE; + } + + g_object_set_data ((GObject *) bin, "validate-monitor", bin_monitor); + + bin_monitor->element_added_id = + g_signal_connect (bin, "element-added", + G_CALLBACK (_validate_bin_element_added), monitor); + + iterator = gst_bin_iterate_elements (bin); + done = FALSE; + while (!done) { + GValue value = { 0, }; + + switch (gst_iterator_next (iterator, &value)) { + case GST_ITERATOR_OK: + element = g_value_get_object (&value); + gst_validate_bin_monitor_wrap_element (bin_monitor, element); + g_value_reset (&value); + break; + case GST_ITERATOR_RESYNC: + /* TODO how to handle this? */ + gst_iterator_resync (iterator); + break; + case GST_ITERATOR_ERROR: + done = TRUE; + break; + case GST_ITERATOR_DONE: + done = TRUE; + break; + } + } + gst_iterator_free (iterator); + + return TRUE; +} + +static void +gst_validate_bin_monitor_wrap_element (GstValidateBinMonitor * monitor, + GstElement * element) +{ + GstValidateElementMonitor *element_monitor; + GST_DEBUG_OBJECT (monitor, "Wrapping element %s", GST_ELEMENT_NAME (element)); + + element_monitor = + GST_VALIDATE_ELEMENT_MONITOR_CAST (gst_validate_monitor_factory_create + (GST_OBJECT_CAST (element), GST_VALIDATE_MONITOR_GET_RUNNER (monitor), + GST_VALIDATE_MONITOR_CAST (monitor))); + g_return_if_fail (element_monitor != NULL); + + GST_VALIDATE_MONITOR_LOCK (monitor); + monitor->element_monitors = g_list_prepend (monitor->element_monitors, + element_monitor); + GST_VALIDATE_MONITOR_UNLOCK (monitor); +} + +static void +_validate_bin_element_added (GstBin * bin, GstElement * element, + GstValidateBinMonitor * monitor) +{ + g_return_if_fail (GST_VALIDATE_ELEMENT_MONITOR_GET_ELEMENT (monitor) == + GST_ELEMENT_CAST (bin)); + gst_validate_bin_monitor_wrap_element (monitor, element); +} diff --git a/validate/gst/validate/gst-validate-bin-monitor.h b/validate/gst/validate/gst-validate-bin-monitor.h new file mode 100644 index 0000000..4d8e92f --- /dev/null +++ b/validate/gst/validate/gst-validate-bin-monitor.h @@ -0,0 +1,84 @@ +/* GStreamer + * Copyright (C) 2013 Thiago Santos + * + * gst-validate-bin-monitor.h - Validate BinMonitor class + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_VALIDATE_BIN_MONITOR_H__ +#define __GST_VALIDATE_BIN_MONITOR_H__ + +#include +#include +#include +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_VALIDATE_BIN_MONITOR (gst_validate_bin_monitor_get_type ()) +#define GST_IS_VALIDATE_BIN_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_VALIDATE_BIN_MONITOR)) +#define GST_IS_VALIDATE_BIN_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_VALIDATE_BIN_MONITOR)) +#define GST_VALIDATE_BIN_MONITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_VALIDATE_BIN_MONITOR, GstValidateBinMonitorClass)) +#define GST_VALIDATE_BIN_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_VALIDATE_BIN_MONITOR, GstValidateBinMonitor)) +#define GST_VALIDATE_BIN_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_VALIDATE_BIN_MONITOR, GstValidateBinMonitorClass)) +#define GST_VALIDATE_BIN_MONITOR_CAST(obj) ((GstValidateBinMonitor*)(obj)) +#define GST_VALIDATE_BIN_MONITOR_CLASS_CAST(klass) ((GstValidateBinMonitorClass*)(klass)) + +#define GST_VALIDATE_BIN_MONITOR_GET_BIN(m) (GST_BIN_CAST (GST_VALIDATE_ELEMENT_MONITOR_GET_ELEMENT (m))) + +typedef struct _GstValidateBinMonitor GstValidateBinMonitor; +typedef struct _GstValidateBinMonitorClass GstValidateBinMonitorClass; + +/** + * GstValidateBinMonitor: + * + * GStreamer Validate BinMonitor class. + * + * Class that wraps a #GstBin for Validate checks + */ +struct _GstValidateBinMonitor { + GstValidateElementMonitor parent; + + GList *element_monitors; + + GstValidateScenario *scenario; + + /*< private >*/ + gulong element_added_id; + gboolean stateless; +}; + +/** + * GstValidateBinMonitorClass: + * @parent_class: parent + * + * GStreamer Validate BinMonitor object class. + */ +struct _GstValidateBinMonitorClass { + GstValidateElementMonitorClass parent_class; +}; + +/* normal GObject stuff */ +GType gst_validate_bin_monitor_get_type (void); + +GstValidateBinMonitor * gst_validate_bin_monitor_new (GstBin * bin, GstValidateRunner * runner, GstValidateMonitor * parent); + +G_END_DECLS + +#endif /* __GST_VALIDATE_BIN_MONITOR_H__ */ + diff --git a/validate/gst/validate/gst-validate-element-monitor.c b/validate/gst/validate/gst-validate-element-monitor.c new file mode 100644 index 0000000..7d9f945 --- /dev/null +++ b/validate/gst/validate/gst-validate-element-monitor.c @@ -0,0 +1,313 @@ +/* GStreamer + * + * Copyright (C) 2013 Collabora Ltd. + * Author: Thiago Sousa Santos + * + * gst-validate-element-monitor.c - Validate ElementMonitor class + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "gst-validate-internal.h" +#include "gst-validate-element-monitor.h" +#include "gst-validate-pad-monitor.h" +#include "gst-validate-monitor-factory.h" +#include "gst-validate-utils.h" +#include "validate.h" +#include + +/** + * SECTION:gst-validate-element-monitor + * @short_description: Class that wraps a #GstElement for Validate checks + * + * TODO + */ + +#define gst_validate_element_monitor_parent_class parent_class +G_DEFINE_TYPE (GstValidateElementMonitor, gst_validate_element_monitor, + GST_TYPE_VALIDATE_MONITOR); + +static void +gst_validate_element_monitor_wrap_pad (GstValidateElementMonitor * monitor, + GstPad * pad); +static gboolean gst_validate_element_monitor_do_setup (GstValidateMonitor * + monitor); +static GstElement *gst_validate_element_monitor_get_element (GstValidateMonitor + * monitor); + +static void +_validate_element_pad_added (GstElement * element, GstPad * pad, + GstValidateElementMonitor * monitor); + +static void +gst_validate_element_set_media_descriptor (GstValidateMonitor * monitor, + GstMediaDescriptor * media_descriptor) +{ + gboolean done; + GstPad *pad; + GstValidateMonitor *pmonitor; + GstIterator *iterator; + + iterator = + gst_element_iterate_pads (GST_ELEMENT (GST_VALIDATE_MONITOR_GET_OBJECT + (monitor))); + done = FALSE; + while (!done) { + GValue value = { 0, }; + + switch (gst_iterator_next (iterator, &value)) { + case GST_ITERATOR_OK: + + pad = g_value_get_object (&value); + + pmonitor = g_object_get_data ((GObject *) pad, "validate-monitor"); + if (pmonitor) + gst_validate_monitor_set_media_descriptor (pmonitor, + media_descriptor); + g_value_reset (&value); + break; + case GST_ITERATOR_RESYNC: + /* TODO how to handle this? */ + gst_iterator_resync (iterator); + break; + case GST_ITERATOR_ERROR: + done = TRUE; + break; + case GST_ITERATOR_DONE: + done = TRUE; + break; + } + } + gst_iterator_free (iterator); +} + + +static void +gst_validate_element_monitor_dispose (GObject * object) +{ + GstValidateElementMonitor *monitor = + GST_VALIDATE_ELEMENT_MONITOR_CAST (object); + + if (GST_VALIDATE_MONITOR_GET_OBJECT (monitor) && monitor->pad_added_id) + g_signal_handler_disconnect (GST_VALIDATE_MONITOR_GET_OBJECT (monitor), + monitor->pad_added_id); + + g_list_free_full (monitor->pad_monitors, g_object_unref); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + + +static void +gst_validate_element_monitor_class_init (GstValidateElementMonitorClass * klass) +{ + GObjectClass *gobject_class; + GstValidateMonitorClass *monitor_klass; + + gobject_class = G_OBJECT_CLASS (klass); + monitor_klass = GST_VALIDATE_MONITOR_CLASS (klass); + + gobject_class->dispose = gst_validate_element_monitor_dispose; + + monitor_klass->setup = gst_validate_element_monitor_do_setup; + monitor_klass->get_element = gst_validate_element_monitor_get_element; + monitor_klass->set_media_descriptor = + gst_validate_element_set_media_descriptor; +} + +static void +gst_validate_element_monitor_init (GstValidateElementMonitor * element_monitor) +{ +} + +/** + * gst_validate_element_monitor_new: + * @element: (transfer-none): a #GstElement to run Validate on + */ +GstValidateElementMonitor * +gst_validate_element_monitor_new (GstElement * element, + GstValidateRunner * runner, GstValidateMonitor * parent) +{ + GstValidateElementMonitor *monitor; + + g_return_val_if_fail (element != NULL, NULL); + + monitor = g_object_new (GST_TYPE_VALIDATE_ELEMENT_MONITOR, "object", element, + "validate-runner", runner, "validate-parent", parent, NULL); + + if (GST_VALIDATE_ELEMENT_MONITOR_GET_ELEMENT (monitor) == NULL) { + g_object_unref (monitor); + return NULL; + } + + return monitor; +} + +static GstElement * +gst_validate_element_monitor_get_element (GstValidateMonitor * monitor) +{ + return GST_VALIDATE_ELEMENT_MONITOR_GET_ELEMENT (monitor); +} + +static void +gst_validate_element_monitor_inspect (GstValidateElementMonitor * monitor) +{ + GstElement *element; + GstElementClass *klass; + const gchar *klassname; + + element = GST_VALIDATE_ELEMENT_MONITOR_GET_ELEMENT (monitor); + klass = GST_ELEMENT_CLASS (G_OBJECT_GET_CLASS (element)); + + + klassname = + gst_element_class_get_metadata (klass, GST_ELEMENT_METADATA_KLASS); + if (klassname) { + monitor->is_decoder = strstr (klassname, "Decoder") != NULL; + monitor->is_encoder = strstr (klassname, "Encoder") != NULL; + monitor->is_demuxer = strstr (klassname, "Demuxer") != NULL; + monitor->is_converter = strstr (klassname, "Converter") != NULL; + } else + GST_ERROR_OBJECT (element, "no klassname"); +} + +static void +set_config_properties (GstValidateMonitor * monitor, GstElement * element) +{ + GList *config, *l; + + config = gst_validate_plugin_get_config (NULL); + for (l = config; l != NULL; l = g_list_next (l)) { + GstStructure *s = l->data; + const gchar *klass; + const gchar *prop_name; + const GValue *prop_value; + + if (g_strcmp0 (gst_structure_get_string (s, "action"), "set-property") != 0) + continue; + + klass = gst_structure_get_string (s, "target-element-klass"); + if (klass && !gst_validate_element_has_klass (element, klass)) + continue; + + prop_name = gst_structure_get_string (s, "property-name"); + if (!prop_name + || !g_object_class_find_property (G_OBJECT_GET_CLASS (element), + prop_name)) + continue; + + prop_value = gst_structure_get_value (s, "property-value"); + if (!prop_value) + continue; + + g_object_set_property (G_OBJECT (element), prop_name, prop_value); + } +} + +static gboolean +gst_validate_element_monitor_do_setup (GstValidateMonitor * monitor) +{ + GstIterator *iterator; + gboolean done; + GstPad *pad; + GstValidateElementMonitor *elem_monitor; + GstElement *element; + + if (!GST_IS_ELEMENT (GST_VALIDATE_MONITOR_GET_OBJECT (monitor))) { + GST_WARNING_OBJECT (monitor, "Trying to create element monitor with other " + "type of object"); + return FALSE; + } + + elem_monitor = GST_VALIDATE_ELEMENT_MONITOR_CAST (monitor); + + GST_DEBUG_OBJECT (monitor, "Setting up monitor for element %" GST_PTR_FORMAT, + GST_VALIDATE_MONITOR_GET_OBJECT (monitor)); + element = GST_VALIDATE_ELEMENT_MONITOR_GET_ELEMENT (monitor); + + if (g_object_get_data ((GObject *) element, "validate-monitor")) { + GST_DEBUG_OBJECT (elem_monitor, + "Pad already has a validate-monitor associated"); + return FALSE; + } + + g_object_set_data ((GObject *) element, "validate-monitor", elem_monitor); + + gst_validate_element_monitor_inspect (elem_monitor); + + elem_monitor->pad_added_id = g_signal_connect (element, "pad-added", + G_CALLBACK (_validate_element_pad_added), monitor); + + iterator = gst_element_iterate_pads (element); + done = FALSE; + while (!done) { + GValue value = { 0, }; + + switch (gst_iterator_next (iterator, &value)) { + case GST_ITERATOR_OK: + pad = g_value_get_object (&value); + gst_validate_element_monitor_wrap_pad (elem_monitor, pad); + g_value_reset (&value); + break; + case GST_ITERATOR_RESYNC: + /* TODO how to handle this? */ + gst_iterator_resync (iterator); + break; + case GST_ITERATOR_ERROR: + done = TRUE; + break; + case GST_ITERATOR_DONE: + done = TRUE; + break; + } + } + gst_iterator_free (iterator); + + set_config_properties (monitor, element); + + return TRUE; +} + +static void +gst_validate_element_monitor_wrap_pad (GstValidateElementMonitor * monitor, + GstPad * pad) +{ + GstValidatePadMonitor *pad_monitor; + GST_DEBUG_OBJECT (monitor, "Wrapping pad %s:%s", GST_DEBUG_PAD_NAME (pad)); + + pad_monitor = + GST_VALIDATE_PAD_MONITOR (gst_validate_monitor_factory_create (GST_OBJECT + (pad), GST_VALIDATE_MONITOR_GET_RUNNER (monitor), + GST_VALIDATE_MONITOR (monitor))); + g_return_if_fail (pad_monitor != NULL); + + GST_VALIDATE_MONITOR_LOCK (monitor); + monitor->pad_monitors = g_list_prepend (monitor->pad_monitors, pad_monitor); + GST_VALIDATE_MONITOR_UNLOCK (monitor); +} + +static void +_validate_element_pad_added (GstElement * element, GstPad * pad, + GstValidateElementMonitor * monitor) +{ + g_return_if_fail (GST_VALIDATE_ELEMENT_MONITOR_GET_ELEMENT (monitor) == + element); + gst_validate_element_monitor_wrap_pad (monitor, pad); +} diff --git a/validate/gst/validate/gst-validate-element-monitor.h b/validate/gst/validate/gst-validate-element-monitor.h new file mode 100644 index 0000000..8ee499b --- /dev/null +++ b/validate/gst/validate/gst-validate-element-monitor.h @@ -0,0 +1,88 @@ +/* GStreamer + * Copyright (C) 2013 Thiago Santos + * + * gst-validate-element-monitor.h - Validate ElementMonitor class + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_VALIDATE_ELEMENT_MONITOR_H__ +#define __GST_VALIDATE_ELEMENT_MONITOR_H__ + +#include +#include + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_VALIDATE_ELEMENT_MONITOR (gst_validate_element_monitor_get_type ()) +#define GST_IS_VALIDATE_ELEMENT_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_VALIDATE_ELEMENT_MONITOR)) +#define GST_IS_VALIDATE_ELEMENT_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_VALIDATE_ELEMENT_MONITOR)) +#define GST_VALIDATE_ELEMENT_MONITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_VALIDATE_ELEMENT_MONITOR, GstValidateElementMonitorClass)) +#define GST_VALIDATE_ELEMENT_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_VALIDATE_ELEMENT_MONITOR, GstValidateElementMonitor)) +#define GST_VALIDATE_ELEMENT_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_VALIDATE_ELEMENT_MONITOR, GstValidateElementMonitorClass)) +#define GST_VALIDATE_ELEMENT_MONITOR_CAST(obj) ((GstValidateElementMonitor*)(obj)) +#define GST_VALIDATE_ELEMENT_MONITOR_CLASS_CAST(klass) ((GstValidateElementMonitorClass*)(klass)) + +#define GST_VALIDATE_ELEMENT_MONITOR_GET_ELEMENT(m) (GST_ELEMENT_CAST (GST_VALIDATE_MONITOR_GET_OBJECT (m))) +#define GST_VALIDATE_ELEMENT_MONITOR_ELEMENT_IS_DECODER(m) (GST_VALIDATE_ELEMENT_MONITOR_CAST (m)->is_decoder) +#define GST_VALIDATE_ELEMENT_MONITOR_ELEMENT_IS_ENCODER(m) (GST_VALIDATE_ELEMENT_MONITOR_CAST (m)->is_encoder) +#define GST_VALIDATE_ELEMENT_MONITOR_ELEMENT_IS_DEMUXER(m) (GST_VALIDATE_ELEMENT_MONITOR_CAST (m)->is_demuxer) +#define GST_VALIDATE_ELEMENT_MONITOR_ELEMENT_IS_CONVERTER(m) (GST_VALIDATE_ELEMENT_MONITOR_CAST (m)->is_converter) + +typedef struct _GstValidateElementMonitor GstValidateElementMonitor; +typedef struct _GstValidateElementMonitorClass GstValidateElementMonitorClass; + +/** + * GstValidateElementMonitor: + * + * GStreamer Validate ElementMonitor class. + * + * Class that wraps a #GstElement for Validate checks + */ +struct _GstValidateElementMonitor { + GstValidateMonitor parent; + + /*< private >*/ + gulong pad_added_id; + GList *pad_monitors; + + gboolean is_decoder; + gboolean is_encoder; + gboolean is_demuxer; + gboolean is_converter; +}; + +/** + * GstValidateElementMonitorClass: + * @parent_class: parent + * + * GStreamer Validate ElementMonitor object class. + */ +struct _GstValidateElementMonitorClass { + GstValidateMonitorClass parent_class; +}; + +/* normal GObject stuff */ +GType gst_validate_element_monitor_get_type (void); + +GstValidateElementMonitor * gst_validate_element_monitor_new (GstElement * element, GstValidateRunner * runner, GstValidateMonitor * parent); + +G_END_DECLS + +#endif /* __GST_VALIDATE_ELEMENT_MONITOR_H__ */ + diff --git a/validate/gst/validate/gst-validate-enums.h b/validate/gst/validate/gst-validate-enums.h new file mode 100644 index 0000000..1ffdcd4 --- /dev/null +++ b/validate/gst/validate/gst-validate-enums.h @@ -0,0 +1,81 @@ +/* GStreamer + * Copyright (C) 2014 Mathieu Duponchelle + * + * gst-validate-enums.h - Validate constants. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_VALIDATE_ENUMS_H__ +#define __GST_VALIDATE_ENUMS_H__ + +/** + * GstValidateReportingDetails: + * @GST_VALIDATE_SHOW_NONE: No debugging level specified or desired. Used to deactivate + * debugging output. + * @GST_VALIDATE_SHOW_SYNTHETIC: Summary of the issues found, with no + * details. + * @GST_VALIDATE_SHOW_SUBCHAIN: If set as the default level, similar + * issues can be reported multiple times for different subchains. + * If set as the level for a particular object (my_object:subchain), validate + * will report the issues where the object is the first to report an issue for + * a subchain. + * @GST_VALIDATE_SHOW_MONITOR: If set as the default level, all the + * distinct issues for all the monitors will be reported. + * If set as the level for a particular object, all the distinct issues for this object + * will be reported. + * Note that if the same issue happens twice on the same object, up until this + * level that issue is only reported once. + * @GST_VALIDATE_SHOW_ALL: All the issues will be reported, even those + * that repeat themselves inside the same object. This can be *very* verbose if + * set globally. + * @GST_VALIDATE_SHOW_UNKNOWN: No reporting level known, + * reporting will default to the global reporting level. + * + * Setting the reporting level allows to control the way issues are reported + * when calling #gst_validate_runner_printf. + * + * The reporting level can be set through the "GST_VALIDATE_REPORTING_DETAILS" + * environment variable, as a comma-separated list of (optional) object categories / names + * and levels. No object category / name sets the global level. + * + * Examples: GST_VALIDATE_REPORTING_DETAILS=synthetic,h264parse:all + * GST_VALIDATE_REPORTING_DETAILS=none,h264parse::sink_0:synthetic + */ +typedef enum { + GST_VALIDATE_SHOW_UNKNOWN = 0, + GST_VALIDATE_SHOW_NONE = 1, + GST_VALIDATE_SHOW_SYNTHETIC = 2, + GST_VALIDATE_SHOW_SUBCHAIN = 3, + GST_VALIDATE_SHOW_MONITOR = 4, + GST_VALIDATE_SHOW_ALL = 5, + GST_VALIDATE_SHOW_COUNT +} GstValidateReportingDetails; + +/** + * GST_VALIDATE_SHOW_DEFAULT: + * + * Defines the default reporting level to be used with gst-validate. It is normally + * set to #GST_VALIDATE_SHOW_SYNTHETIC so only a synthetic report + * gets printed. + * As it can be configured at compile time, developer builds may chose to + * override that though. + */ +#ifndef GST_VALIDATE_SHOW_DEFAULT +#define GST_VALIDATE_SHOW_DEFAULT GST_VALIDATE_SHOW_SYNTHETIC +#endif + +#endif /* __GST_VALIDATE_RUNNER_H__ */ diff --git a/validate/gst/validate/gst-validate-i18n-lib.h b/validate/gst/validate/gst-validate-i18n-lib.h new file mode 100644 index 0000000..f01f677 --- /dev/null +++ b/validate/gst/validate/gst-validate-i18n-lib.h @@ -0,0 +1,47 @@ +/* GStreamer + * Copyright (C) 2004 Thomas Vander Stichele + * + * gst-validate-i18n-lib.h: internationalization macros for the GStreamer libraries + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#ifndef __GST_VALIDATE_I18N_LIB_H__ +#define __GST_VALIDATE_I18N_LIB_H__ + +#ifndef PACKAGE_NAME +#error You must include config.h before including this header. +#endif + +#ifdef ENABLE_NLS + +#include /* some people need it and some people don't */ +#include "gettext.h" /* included with gettext distribution and copied */ + +/* we want to use shorthand _() for translating and N_() for marking */ +#define _(String) dgettext (GETTEXT_PACKAGE, String) +#define N_(String) gettext_noop (String) +/* FIXME: if we need it, we can add Q_ as well, like in glib */ + +#else +#define _(String) String +#define N_(String) String +#define ngettext(Singular,Plural,Count) ((Count>1)?Plural:Singular) + +#endif + +#endif /* __GST_VALIDATE_I18N_LIB_H__ */ diff --git a/validate/gst/validate/gst-validate-internal.h b/validate/gst/validate/gst-validate-internal.h new file mode 100644 index 0000000..adabd21 --- /dev/null +++ b/validate/gst/validate/gst-validate-internal.h @@ -0,0 +1,48 @@ +/* GStreamer + * Copyright (C) 2013 Thiago Santos + * + * validate.c - Validate generic functions + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_VALIDATE_INTERNAL_H__ +#define __GST_VALIDATE_INTERNAL_H__ + +#include +#include "gst-validate-scenario.h" + +GST_DEBUG_CATEGORY_EXTERN (gstvalidate_debug); +#define GST_CAT_DEFAULT gstvalidate_debug + +extern GRegex *newline_regex; + + +/* If an action type is 1 (TRUE) we also concider it is a config to keep backward compatibility */ +#define IS_CONFIG_ACTION_TYPE(type) (((type) & GST_VALIDATE_ACTION_TYPE_CONFIG) || ((type) == TRUE)) + +GST_EXPORT GType _gst_validate_action_type_type; + +void init_scenarios (void); + +/* FIXME 2.0 Remove that as this is only for backward compatibility + * as we used to have to print actions in the action execution function + * and this is done by the scenario itself now */ +GST_EXPORT gboolean _action_check_and_set_printed (GstValidateAction *action); +GST_EXPORT gboolean gst_validate_action_is_subaction (GstValidateAction *action); +void _priv_validate_override_registry_deinit (void); + +#endif diff --git a/validate/gst/validate/gst-validate-media-info.c b/validate/gst/validate/gst-validate-media-info.c new file mode 100644 index 0000000..b03b66d --- /dev/null +++ b/validate/gst/validate/gst-validate-media-info.c @@ -0,0 +1,1128 @@ +/* GStreamer + * + * Copyright (C) 2013 Collabora Ltd. + * Author: Thiago Sousa Santos + * + * gst-validate-media-info.c + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "gst-validate-media-info.h" + +#include +#include + +struct _GstValidateStreamInfo +{ + GstCaps *caps; + + GList *children; +}; + +static GstValidateStreamInfo * +gst_validate_stream_info_from_discoverer_info (GstDiscovererStreamInfo * info) +{ + GstValidateStreamInfo *ret = g_new0 (GstValidateStreamInfo, 1); + + ret->caps = gst_discoverer_stream_info_get_caps (info); + if (GST_IS_DISCOVERER_CONTAINER_INFO (info)) { + GList *streams = + gst_discoverer_container_info_get_streams (GST_DISCOVERER_CONTAINER_INFO + (info)); + GList *iter; + + for (iter = streams; iter; iter = g_list_next (iter)) { + ret->children = g_list_append (ret->children, + gst_validate_stream_info_from_discoverer_info (iter->data)); + } + gst_discoverer_stream_info_list_free (streams); + } + + return ret; +} + +static GstValidateStreamInfo * +gst_validate_stream_info_from_caps_string (gchar * capsstr) +{ + GstValidateStreamInfo *ret = g_new0 (GstValidateStreamInfo, 1); + + ret->caps = gst_caps_from_string (capsstr); + + return ret; +} + +static void +gst_validate_stream_info_free (GstValidateStreamInfo * si) +{ + if (si->caps) + gst_caps_unref (si->caps); + g_list_free_full (si->children, + (GDestroyNotify) gst_validate_stream_info_free); + g_free (si); +} + +void +gst_validate_media_info_init (GstValidateMediaInfo * mi) +{ + mi->uri = NULL; + mi->file_size = 0; + mi->duration = GST_CLOCK_TIME_NONE; + mi->seekable = FALSE; + mi->stream_info = NULL; + mi->playback_error = NULL; + mi->reverse_playback_error = NULL; + mi->track_switch_error = NULL; + mi->is_image = FALSE; + mi->discover_only = FALSE; +} + +void +gst_validate_media_info_clear (GstValidateMediaInfo * mi) +{ + g_free (mi->uri); + g_free (mi->playback_error); + g_free (mi->reverse_playback_error); + g_free (mi->track_switch_error); + if (mi->stream_info) + gst_validate_stream_info_free (mi->stream_info); +} + +void +gst_validate_media_info_free (GstValidateMediaInfo * mi) +{ + gst_validate_media_info_clear (mi); + g_free (mi); +} + +gchar * +gst_validate_media_info_to_string (GstValidateMediaInfo * mi, gsize * length) +{ + GKeyFile *kf = g_key_file_new (); + gchar *data = NULL; + gchar *str; + + /* file info */ + g_key_file_set_string (kf, "file-info", "uri", mi->uri); + g_key_file_set_uint64 (kf, "file-info", "file-size", mi->file_size); + + /* media info */ + g_key_file_set_uint64 (kf, "media-info", "file-duration", mi->duration); + g_key_file_set_boolean (kf, "media-info", "seekable", mi->seekable); + g_key_file_set_boolean (kf, "media-info", "is-image", mi->is_image); + + if (mi->stream_info && mi->stream_info->caps) { + str = gst_caps_to_string (mi->stream_info->caps); + g_key_file_set_string (kf, "media-info", "caps", str); + g_free (str); + } + + /* playback tests */ + g_key_file_set_string (kf, "playback-tests", "playback-error", + mi->playback_error ? mi->playback_error : ""); + g_key_file_set_string (kf, "playback-tests", "reverse-playback-error", + mi->reverse_playback_error ? mi->reverse_playback_error : ""); + g_key_file_set_string (kf, "playback-tests", "track-switch-error", + mi->track_switch_error ? mi->track_switch_error : ""); + + data = g_key_file_to_data (kf, length, NULL); + g_key_file_free (kf); + + return data; +} + +gboolean +gst_validate_media_info_save (GstValidateMediaInfo * mi, const gchar * path, + GError ** err) +{ + gchar *data = NULL; + gsize datalength = 0; + + data = gst_validate_media_info_to_string (mi, &datalength); + + g_file_set_contents (path, data, datalength, err); + if (err) + return FALSE; + return TRUE; +} + +GstValidateMediaInfo * +gst_validate_media_info_load (const gchar * path, GError ** err) +{ + GKeyFile *kf = g_key_file_new (); + GstValidateMediaInfo *mi; + gchar *str; + + if (!g_key_file_load_from_file (kf, path, G_KEY_FILE_NONE, err)) { + g_key_file_free (kf); + return NULL; + } + + mi = g_new (GstValidateMediaInfo, 1); + gst_validate_media_info_init (mi); + + mi->uri = g_key_file_get_string (kf, "file-info", "uri", err); + if (err && *err) + goto end; + mi->file_size = g_key_file_get_uint64 (kf, "file-info", "file-size", err); + if (err && *err) + goto end; + + mi->duration = + g_key_file_get_uint64 (kf, "media-info", "file-duration", NULL); + mi->seekable = g_key_file_get_boolean (kf, "media-info", "seekable", NULL); + mi->is_image = g_key_file_get_boolean (kf, "media-info", "is-image", NULL); + + str = g_key_file_get_string (kf, "media-info", "caps", NULL); + if (str) { + mi->stream_info = gst_validate_stream_info_from_caps_string (str); + g_free (str); + } + + mi->playback_error = + g_key_file_get_string (kf, "playback-tests", "playback-error", NULL); + mi->reverse_playback_error = + g_key_file_get_string (kf, "playback-tests", "reverse-playback-error", + NULL); + mi->track_switch_error = + g_key_file_get_string (kf, "playback-tests", "track-switch-error", NULL); + if (mi->playback_error && strlen (mi->playback_error) == 0) { + g_free (mi->playback_error); + mi->playback_error = NULL; + } + if (mi->reverse_playback_error && strlen (mi->reverse_playback_error) == 0) { + g_free (mi->reverse_playback_error); + mi->reverse_playback_error = NULL; + } + if (mi->track_switch_error && strlen (mi->track_switch_error) == 0) { + g_free (mi->track_switch_error); + mi->track_switch_error = NULL; + } + +end: + g_key_file_free (kf); + return mi; +} + +static gboolean +check_file_size (GstValidateMediaInfo * mi) +{ + GStatBuf statbuf; + gchar *filepath; + guint64 size = 0; + gboolean ret = TRUE; + GError *err = NULL; + + filepath = g_filename_from_uri (mi->uri, NULL, &err); + if (!filepath) { + g_error_free (err); + return FALSE; + } + + if (g_stat (filepath, &statbuf) == 0) { + size = statbuf.st_size; + } else { + ret = FALSE; + goto end; + } + + mi->file_size = size; + +end: + g_free (filepath); + return ret; +} + +static gboolean +check_file_duration (GstValidateMediaInfo * mi, GstDiscovererInfo * info) +{ + mi->duration = gst_discoverer_info_get_duration (info); + return TRUE; +} + +static gboolean +check_seekable (GstValidateMediaInfo * mi, GstDiscovererInfo * info) +{ + mi->seekable = gst_discoverer_info_get_seekable (info); + return TRUE; +} + +#if 0 +static inline gboolean +_gst_caps_can_intersect_safe (const GstCaps * a, const GstCaps * b) +{ + if (a == b) + return TRUE; + if ((a == NULL) || (b == NULL)) + return FALSE; + return gst_caps_can_intersect (a, b); +} + +#if 0 +typedef struct +{ + GstEncodingProfile *profile; + gint count; +} ExpectedStream; + +#define SET_MESSAGE(placeholder, msg) \ +G_STMT_START { \ + if (placeholder) { \ + *placeholder = msg; \ + } \ +} G_STMT_END + +static gboolean +compare_encoding_profile_with_discoverer_stream (GstValidateFileChecker * fc, + GstEncodingProfile * prof, GstDiscovererStreamInfo * stream, gchar ** msg); + +static gboolean + compare_container_profile_with_container_discoverer_stream + (GstValidateFileChecker * fc, GstEncodingContainerProfile * prof, + GstDiscovererContainerInfo * stream, gchar ** msg) +{ + ExpectedStream *expected_streams = NULL; + GList *container_streams; + const GList *profile_iter; + const GList *streams_iter; + gint i; + gint expected_count = g_list_length ((GList *) + gst_encoding_container_profile_get_profiles (prof)); + gboolean ret = TRUE; + + container_streams = gst_discoverer_container_info_get_streams (stream); + + if (expected_count == 0) { + if (g_list_length (container_streams) != 0) { + SET_MESSAGE (msg, + g_strdup_printf + ("No streams expected on this container, but found %u", + g_list_length (container_streams))); + ret = FALSE; + goto end; + } + } + + /* initialize expected streams data */ + expected_streams = g_malloc0 (sizeof (ExpectedStream) * expected_count); + for (i = 0, profile_iter = gst_encoding_container_profile_get_profiles (prof); + profile_iter; profile_iter = g_list_next (profile_iter), i++) { + GstEncodingProfile *prof = profile_iter->data; + ExpectedStream *expected = &(expected_streams[i]); + + expected->profile = prof; + } + + /* look for the streams on discoverer info */ + for (streams_iter = container_streams; streams_iter; + streams_iter = g_list_next (streams_iter)) { + GstDiscovererStreamInfo *info = streams_iter->data; + gboolean found = FALSE; + for (i = 0; i < expected_count; i++) { + ExpectedStream *expected = &(expected_streams[i]); + + if (compare_encoding_profile_with_discoverer_stream (fc, + expected->profile, info, NULL)) { + found = TRUE; + break; + } + } + + if (!found) { + GstCaps *caps = gst_discoverer_stream_info_get_caps (info); + gchar *caps_str = gst_caps_to_string (caps); + SET_MESSAGE (msg, + g_strdup_printf ("Stream with caps '%s' wasn't found on file", + caps_str)); + g_free (caps_str); + gst_caps_unref (caps); + ret = FALSE; + goto end; + } + } + + /* check if all expected streams are present */ + for (i = 0; i < expected_count; i++) { + ExpectedStream *expected = &(expected_streams[i]); + guint presence = gst_encoding_profile_get_presence (expected->profile); + + if (presence == 0) + continue; + + if (presence != expected->count) { + gchar *caps_str = + gst_caps_to_string (gst_encoding_profile_get_format + (expected->profile)); + SET_MESSAGE (msg, + g_strdup_printf ("Stream from profile %s (with caps '%s" + "' has presence %u but the number of streams found was %d", + gst_encoding_profile_get_name (expected->profile), caps_str, + presence, expected->count)); + g_free (caps_str); + ret = FALSE; + goto end; + } + } + +end: + g_free (expected_streams); + gst_discoverer_stream_info_list_free (container_streams); + return ret; +} + +static gboolean +compare_encoding_profile_with_discoverer_stream (GstValidateFileChecker * fc, + GstEncodingProfile * prof, GstDiscovererStreamInfo * stream, gchar ** msg) +{ + gboolean ret = TRUE; + GstCaps *caps = NULL; + const GstCaps *profile_caps; + const GstCaps *restriction_caps; + + caps = gst_discoverer_stream_info_get_caps (stream); + profile_caps = gst_encoding_profile_get_format (prof); + restriction_caps = gst_encoding_profile_get_restriction (prof); + + /* TODO need to consider profile caps restrictions */ + if (!_gst_caps_can_intersect_safe (caps, profile_caps)) { + gchar *caps_str = gst_caps_to_string (caps); + gchar *profile_caps_str = gst_caps_to_string (profile_caps); + SET_MESSAGE (msg, g_strdup_printf ("Caps '%s' didn't match profile '%s'", + profile_caps_str, caps_str)); + g_free (caps_str); + g_free (profile_caps_str); + ret = FALSE; + goto end; + } + + if (restriction_caps) { + GstStructure *structure; + gint i; + gboolean found = FALSE; + + for (i = 0; i < gst_caps_get_size (restriction_caps); i++) { + structure = gst_caps_get_structure (restriction_caps, i); + structure = gst_structure_copy (structure); + gst_structure_set_name (structure, + gst_structure_get_name (gst_caps_get_structure (caps, 0))); + if (gst_structure_can_intersect (structure, gst_caps_get_structure (caps, + 0))) { + gst_structure_free (structure); + found = TRUE; + break; + } + gst_structure_free (structure); + } + if (!found) { + gchar *caps_str = gst_caps_to_string (caps); + gchar *restriction_caps_str = gst_caps_to_string (restriction_caps); + SET_MESSAGE (msg, + g_strdup_printf ("Caps restriction '%s' wasn't respected on file " + "with caps '%s'", restriction_caps_str, caps_str)); + g_free (caps_str); + g_free (restriction_caps_str); + ret = FALSE; + goto end; + } + } + + if (GST_IS_ENCODING_CONTAINER_PROFILE (prof)) { + if (GST_IS_DISCOVERER_CONTAINER_INFO (stream)) { + ret = + ret & compare_container_profile_with_container_discoverer_stream (fc, + (GstEncodingContainerProfile *) prof, + (GstDiscovererContainerInfo *) stream, msg); + } else { + SET_MESSAGE (msg, + g_strdup_printf ("Expected container profile but found stream of %s", + gst_discoverer_stream_info_get_stream_type_nick (stream))); + ret = FALSE; + goto end; + } + + } else if (GST_IS_ENCODING_VIDEO_PROFILE (prof)) { + if (!GST_IS_DISCOVERER_VIDEO_INFO (stream)) { + SET_MESSAGE (msg, + g_strdup_printf ("Expected video profile but found stream of %s", + gst_discoverer_stream_info_get_stream_type_nick (stream))); + ret = FALSE; + goto end; + } + + } else if (GST_IS_ENCODING_AUDIO_PROFILE (prof)) { + if (!GST_IS_DISCOVERER_AUDIO_INFO (stream)) { + SET_MESSAGE (msg, + g_strdup_printf ("Expected audio profile but found stream of %s", + gst_discoverer_stream_info_get_stream_type_nick (stream))); + ret = FALSE; + goto end; + } + } else { + g_assert_not_reached (); + return FALSE; + } + + +end: + if (caps) + gst_caps_unref (caps); + + return ret; +} +#endif +#endif + +static gboolean +check_encoding_profile (GstValidateMediaInfo * mi, GstDiscovererInfo * info) +{ + gboolean ret = TRUE; + GstDiscovererStreamInfo *streaminfo; + + streaminfo = gst_discoverer_info_get_stream_info (info); + mi->stream_info = gst_validate_stream_info_from_discoverer_info (streaminfo); + + gst_discoverer_info_unref (streaminfo); + + return ret; +} + +typedef gboolean (*GstElementConfigureFunc) (GstValidateMediaInfo *, + GstElement *, gchar ** msg); +static gboolean +check_playback_scenario (GstValidateMediaInfo * mi, + GstElementConfigureFunc configure_function, gchar ** error_message) +{ + GstElement *playbin; + GstElement *videosink, *audiosink; + GstBus *bus; + GstMessage *msg; + gboolean ret = TRUE; + GstStateChangeReturn state_ret; + + playbin = gst_element_factory_make ("playbin", "fc-playbin"); + videosink = gst_element_factory_make ("fakesink", "fc-videosink"); + audiosink = gst_element_factory_make ("fakesink", "fc-audiosink"); + + if (!playbin || !videosink || !audiosink) { + *error_message = g_strdup ("Playbin and/or fakesink not available"); + } + + g_object_set (playbin, "video-sink", videosink, "audio-sink", audiosink, + "uri", mi->uri, NULL); + + bus = gst_pipeline_get_bus (GST_PIPELINE (playbin)); + + state_ret = gst_element_set_state (playbin, GST_STATE_PAUSED); + if (state_ret == GST_STATE_CHANGE_FAILURE) { + *error_message = g_strdup ("Failed to change pipeline to paused"); + ret = FALSE; + goto end; + } else if (state_ret == GST_STATE_CHANGE_ASYNC) { + msg = + gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE, + GST_MESSAGE_ASYNC_DONE | GST_MESSAGE_EOS | GST_MESSAGE_ERROR); + if (msg && GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ASYNC_DONE) { + gst_message_unref (msg); + } else { + ret = FALSE; + *error_message = g_strdup ("Playback finihshed unexpectedly"); + goto end; + } + } + + if (configure_function) { + if (!configure_function (mi, playbin, error_message)) { + gst_object_unref (bus); + gst_object_unref (playbin); + return FALSE; + } + } + + if (gst_element_set_state (playbin, + GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { + *error_message = g_strdup ("Failed to set pipeline to playing"); + ret = FALSE; + goto end; + } + + msg = + gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE, + GST_MESSAGE_ERROR | GST_MESSAGE_EOS); + if (msg) { + if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_EOS) { + /* all good */ + ret = TRUE; + } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) { + GError *error = NULL; + gchar *debug = NULL; + + gst_message_parse_error (msg, &error, &debug); + *error_message = g_strdup_printf ("Playback error: %s : %s", + error->message, debug); + g_error_free (error); + g_free (debug); + + ret = FALSE; + } else { + g_assert_not_reached (); + } + gst_message_unref (msg); + } else { + ret = FALSE; + *error_message = g_strdup ("Playback finihshed unexpectedly"); + } + +end: + gst_object_unref (bus); + gst_element_set_state (playbin, GST_STATE_NULL); + gst_object_unref (playbin); + + return ret; +} + +static gboolean +check_playback (GstValidateMediaInfo * mi, gchar ** msg) +{ + return check_playback_scenario (mi, NULL, msg); +} + +static gboolean +send_reverse_seek (GstValidateMediaInfo * mi, GstElement * pipeline, + gchar ** msg) +{ + gboolean ret; + + ret = gst_element_seek (pipeline, -1.0, GST_FORMAT_TIME, + GST_SEEK_FLAG_FLUSH, GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_SET, -1); + + if (!ret) { + *msg = g_strdup ("Reverse playback seek failed"); + } + return ret; +} + +static gboolean +check_reverse_playback (GstValidateMediaInfo * mi, gchar ** msg) +{ + return check_playback_scenario (mi, send_reverse_seek, msg); +} + +typedef struct +{ + guint counter; + guint back_counter; + gulong probe_id; + GstPad *pad; +} BufferCountData; + +static GstPadProbeReturn +input_selector_pad_probe (GstPad * pad, GstPadProbeInfo * info, + gpointer userdata) +{ + GstPad *sink_pad = NULL; + + if (info->type == GST_PAD_PROBE_TYPE_BUFFER) { + BufferCountData *bcd = + g_object_get_data (G_OBJECT (pad), "buffer-count-data"); + if (!bcd) { + GST_ERROR_OBJECT (pad, "No buffer-count-data found"); + return GST_PAD_PROBE_OK; + } + + ++bcd->counter; + if (GST_PAD_IS_SRC (pad)) { + g_object_get (GST_PAD_PARENT (pad), "active-pad", &sink_pad, NULL); + if (sink_pad) { + bcd = g_object_get_data (G_OBJECT (sink_pad), "buffer-count-data"); + if (!bcd) { + gst_object_unref (sink_pad); + GST_ERROR_OBJECT (pad, "No buffer-count-data found"); + return GST_PAD_PROBE_OK; + } + ++bcd->back_counter; + gst_object_unref (sink_pad); + } + } + } + return GST_PAD_PROBE_OK; +} + +static void +setup_input_selector_counters (GstElement * element) +{ + GstIterator *iterator; + gboolean done = FALSE; + GValue value = { 0, }; + GstPad *pad; + BufferCountData *bcd; + + iterator = gst_element_iterate_pads (element); + while (!done) { + switch (gst_iterator_next (iterator, &value)) { + case GST_ITERATOR_OK: + pad = g_value_dup_object (&value); + bcd = g_slice_new0 (BufferCountData); + g_object_set_data (G_OBJECT (pad), "buffer-count-data", bcd); + bcd->probe_id = gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_BUFFER, + (GstPadProbeCallback) input_selector_pad_probe, NULL, NULL); + bcd->pad = pad; + g_value_reset (&value); + break; + case GST_ITERATOR_RESYNC: + gst_iterator_resync (iterator); + break; + case GST_ITERATOR_ERROR: + done = TRUE; + break; + case GST_ITERATOR_DONE: + done = TRUE; + break; + } + } + gst_iterator_free (iterator); +} + +static gboolean +check_and_remove_input_selector_counters (GstElement * element, + gchar ** error_message) +{ + GstIterator *iterator; + gboolean done = FALSE; + GstPad *pad; + GValue value = { 0, }; + guint id, ncounters = 0, total_sink_count = 0; + BufferCountData *bcd, **bcds = + g_malloc0 (sizeof (BufferCountData *) * element->numpads); + gboolean ret = TRUE; + + /* First gather all counts, and free memory, etc */ + iterator = gst_element_iterate_pads (element); + while (!done) { + switch (gst_iterator_next (iterator, &value)) { + case GST_ITERATOR_OK: + pad = g_value_get_object (&value); + bcd = g_object_get_data (G_OBJECT (pad), "buffer-count-data"); + if (GST_PAD_IS_SINK (pad)) { + bcds[++ncounters] = bcd; + total_sink_count += bcd->counter; + } else { + bcds[0] = bcd; + } + gst_pad_remove_probe (pad, bcd->probe_id); + g_value_reset (&value); + break; + case GST_ITERATOR_RESYNC: + gst_iterator_resync (iterator); + break; + case GST_ITERATOR_ERROR: + done = TRUE; + *error_message = g_strdup ("Failed to iterate through pads"); + ret = FALSE; + break; + case GST_ITERATOR_DONE: + done = TRUE; + break; + } + } + gst_iterator_free (iterator); + + if (!ret) { + g_free (bcds); + return FALSE; + } + + /* Now bcd[0] contains the total number of buffers received, + and subsequent bcd slots contain the total number of buffers sent + by each source pad. Check that the totals match, and that every + source pad got at least one buffer. + Or that's the theory. It doesn't work in practice, the number of + raw buffers flowing is non deterministic. */ +#if 0 + if (bcds[0]->counter != total_sink_count) { + *error_message = g_strdup_printf ("%u buffers received, %u buffers sent", + total_sink_count, bcds[0]->counter); + ret = FALSE; + } + for (id = 1; id < element->numpads; ++id) { + if (bcds[id]->counter == 0) { + *error_message = + g_strdup_printf ("Sink pad %s got no buffers", + GST_PAD_NAME (bcds[id]->pad)); + ret = FALSE; + } + } +#endif + /* We at least check that at least one buffer was sent while the + selected sink was a given sink, for all sinks */ + for (id = 1; id < element->numpads; ++id) { + if (bcds[id]->back_counter == 0) { + *error_message = + g_strdup_printf ("No buffer was sent while sink pad %s was active", + GST_PAD_NAME (bcds[id]->pad)); + ret = FALSE; + } + } + + for (id = 0; id < element->numpads; ++id) { + gst_object_unref (bcds[id]->pad); + g_slice_free (BufferCountData, bcds[id]); + } + g_free (bcds); + return ret; +} + +static GstPad * +find_next_pad (GstElement * element, GstPad * pad) +{ + GstIterator *iterator; + gboolean done = FALSE, pick = FALSE; + GstPad *tmp, *next = NULL, *first = NULL; + GValue value = { 0, }; + + iterator = gst_element_iterate_sink_pads (element); + + while (!done) { + switch (gst_iterator_next (iterator, &value)) { + case GST_ITERATOR_OK: + tmp = g_value_dup_object (&value); + if (first == NULL) + first = gst_object_ref (tmp); + if (pick) { + next = tmp; + done = TRUE; + } else { + pick = (tmp == pad); + gst_object_unref (tmp); + } + g_value_reset (&value); + break; + case GST_ITERATOR_RESYNC: + if (next) { + gst_object_unref (next); + next = NULL; + if (first) { + gst_object_unref (first); + first = NULL; + } + pick = FALSE; + } + gst_iterator_resync (iterator); + break; + case GST_ITERATOR_ERROR: + done = TRUE; + break; + case GST_ITERATOR_DONE: + /* When we reach the end, we may be in the case where the pad + to search from was the last one in the list, in which case + we want to return the first pad. */ + if (pick) { + next = first; + first = NULL; + } + done = TRUE; + break; + } + } + gst_iterator_free (iterator); + if (first) + gst_object_unref (first); + return next; +} + +static int +find_input_selector (GValue * value, void *userdata) +{ + GstElement *element = g_value_get_object (value); + g_assert (GST_IS_ELEMENT (element)); + if (g_str_has_prefix (GST_ELEMENT_NAME (element), "inputselector")) { + guint npads; + g_object_get (element, "n-pads", &npads, NULL); + if (npads > 1) + return 0; + } + return !0; +} + +/* This function looks for an input-selector, and, if one is found, + cycle through its sink pads */ +static gboolean +check_track_selection (GstValidateMediaInfo * mi, gchar ** error_message) +{ + GstElement *playbin; + GstElement *videosink, *audiosink; + GstElement *input_selector = NULL; + GstBus *bus; + GstMessage *msg; + gboolean ret = TRUE; + GstStateChangeReturn state_ret; + GstIterator *iterator; + GstPad *original_pad; + static const GstClockTime switch_delay = GST_SECOND * 5; + GValue value = { 0, }; + + playbin = gst_element_factory_make ("playbin", "fc-playbin"); + videosink = gst_element_factory_make ("fakesink", "fc-videosink"); + audiosink = gst_element_factory_make ("fakesink", "fc-audiosink"); + + if (!playbin || !videosink || !audiosink) { + *error_message = g_strdup ("Playbin and/or fakesink not available"); + } + + g_object_set (playbin, "video-sink", videosink, "audio-sink", audiosink, + "uri", mi->uri, NULL); + g_object_set (videosink, "sync", TRUE, NULL); + g_object_set (audiosink, "sync", TRUE, NULL); + + bus = gst_pipeline_get_bus (GST_PIPELINE (playbin)); + + state_ret = gst_element_set_state (playbin, GST_STATE_PAUSED); + if (state_ret == GST_STATE_CHANGE_FAILURE) { + *error_message = g_strdup ("Failed to change pipeline to paused"); + ret = FALSE; + goto end; + } else if (state_ret == GST_STATE_CHANGE_ASYNC) { + msg = + gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE, + GST_MESSAGE_ASYNC_DONE | GST_MESSAGE_EOS | GST_MESSAGE_ERROR); + if (msg && GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ASYNC_DONE) { + gst_message_unref (msg); + } else { + ret = FALSE; + *error_message = g_strdup ("Playback finihshed unexpectedly"); + goto end; + } + } + + if (gst_element_set_state (playbin, + GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { + *error_message = g_strdup ("Failed to set pipeline to playing"); + ret = FALSE; + goto end; + } + + iterator = gst_bin_iterate_recurse (GST_BIN (playbin)); + if (!gst_iterator_find_custom (iterator, + (GCompareFunc) find_input_selector, &value, NULL)) { + /* It's fine, there's only one if several tracks of the same type */ + gst_iterator_free (iterator); + input_selector = NULL; + goto end; + } + input_selector = g_value_dup_object (&value); + g_value_reset (&value); + gst_iterator_free (iterator); + g_object_get (input_selector, "active-pad", &original_pad, NULL); + if (!original_pad) { + /* Unexpected, log an error somehow ? */ + ret = FALSE; + gst_object_unref (input_selector); + input_selector = NULL; + goto end; + } + + /* Attach a buffer counter to each pad */ + setup_input_selector_counters (input_selector); + + while (1) { + msg = + gst_bus_timed_pop_filtered (bus, switch_delay, + GST_MESSAGE_ERROR | GST_MESSAGE_EOS); + if (msg) { + if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_EOS) { + /* all good */ + ret = TRUE; + } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) { + GError *error = NULL; + gchar *debug = NULL; + + gst_message_parse_error (msg, &error, &debug); + *error_message = g_strdup_printf ("Playback error: %s : %s", + error->message, debug); + g_error_free (error); + g_free (debug); + + ret = FALSE; + } else { + g_assert_not_reached (); + } + gst_message_unref (msg); + } else { + /* Timeout, switch track if we have more, or stop */ + GstPad *active_pad, *next_pad; + + g_object_get (input_selector, "active-pad", &active_pad, NULL); + if (!active_pad) { + *error_message = + g_strdup ("Failed to get active-pad from input-selector"); + ret = FALSE; + goto end; + } + next_pad = find_next_pad (input_selector, active_pad); + gst_object_unref (active_pad); + if (!next_pad) { + ret = FALSE; + goto end; + } + if (next_pad == original_pad) { + goto end; + } + g_object_set (input_selector, "active-pad", next_pad, NULL); + gst_object_unref (next_pad); + } + } + +end: + if (input_selector) { + if (!check_and_remove_input_selector_counters (input_selector, + error_message)) + ret = FALSE; + gst_object_unref (input_selector); + } + gst_object_unref (bus); + gst_element_set_state (playbin, GST_STATE_NULL); + gst_object_unref (playbin); + + return ret; +} + +static gboolean +check_is_image (GstDiscovererInfo * info) +{ + gboolean ret = FALSE; + GList *video_streams = gst_discoverer_info_get_video_streams (info); + + if (g_list_length (video_streams) == 1) { + if (gst_discoverer_video_info_is_image (video_streams->data)) { + GList *audio_streams = gst_discoverer_info_get_audio_streams (info); + + if (audio_streams == NULL) + ret = TRUE; + else + gst_discoverer_stream_info_list_free (audio_streams); + } + } + + gst_discoverer_stream_info_list_free (video_streams); + + return ret; +} + +gboolean +gst_validate_media_info_inspect_uri (GstValidateMediaInfo * mi, + const gchar * uri, gboolean discover_only, GError ** err) +{ + GstDiscovererInfo *info; + GstDiscoverer *discoverer = gst_discoverer_new (GST_SECOND * 60, err); + gboolean ret = TRUE; + + g_return_val_if_fail (uri != NULL, FALSE); + + g_free (mi->uri); + mi->uri = g_strdup (uri); + + if (!discoverer) { + return FALSE; + } + + info = gst_discoverer_discover_uri (discoverer, uri, err); + + if (gst_discoverer_info_get_result (info) != GST_DISCOVERER_OK) { + gst_object_unref (discoverer); + return FALSE; + } + + mi->is_image = check_is_image (info); + ret = check_file_size (mi) & ret; + ret = check_encoding_profile (mi, info) & ret; + ret = check_file_duration (mi, info) & ret; + + if (mi->is_image) + goto done; + + check_seekable (mi, info); + if (discover_only) + goto done; + + ret = check_playback (mi, &mi->playback_error) & ret; + ret = check_reverse_playback (mi, &mi->reverse_playback_error) & ret; + ret = check_track_selection (mi, &mi->track_switch_error) & ret; + +done: + gst_object_unref (discoverer); + + return ret; +} + +gboolean +gst_validate_media_info_compare (GstValidateMediaInfo * expected, + GstValidateMediaInfo * extracted) +{ + gboolean ret = TRUE; + if (expected->duration != extracted->duration) { + g_print ("Duration changed: %" GST_TIME_FORMAT " -> %" GST_TIME_FORMAT "\n", + GST_TIME_ARGS (expected->duration), + GST_TIME_ARGS (extracted->duration)); + ret = FALSE; + } + if (expected->file_size != extracted->file_size) { + g_print ("File size changed: %" G_GUINT64_FORMAT " -> %" G_GUINT64_FORMAT + "\n", expected->file_size, extracted->file_size); + ret = FALSE; + } + if (expected->seekable && !extracted->seekable) { + g_print ("File isn't seekable anymore\n"); + ret = FALSE; + } + + if (extracted->discover_only == FALSE) { + if (expected->playback_error == NULL && extracted->playback_error) { + g_print ("Playback is now failing with: %s\n", extracted->playback_error); + ret = FALSE; + } + if (expected->reverse_playback_error == NULL + && extracted->reverse_playback_error) { + g_print ("Reverse playback is now failing with: %s\n", + extracted->reverse_playback_error); + ret = FALSE; + } + if (expected->track_switch_error == NULL && extracted->track_switch_error) { + g_print ("Track switching is now failing with: %s\n", + extracted->track_switch_error); + ret = FALSE; + } + } + + if (extracted->stream_info == NULL || expected->stream_info == NULL) { + g_print ("Stream infos could not be retrived, an error occured\n"); + ret = FALSE; + } else if (expected->stream_info + && !gst_caps_is_equal_fixed (expected->stream_info->caps, + extracted->stream_info->caps)) { + gchar *caps1 = gst_caps_to_string (expected->stream_info->caps); + gchar *caps2 = gst_caps_to_string (extracted->stream_info->caps); + + g_print ("Media caps changed: '%s' -> '%s'\n", caps1, caps2); + g_free (caps1); + g_free (caps2); + ret = FALSE; + } + return ret; +} diff --git a/validate/gst/validate/gst-validate-media-info.h b/validate/gst/validate/gst-validate-media-info.h new file mode 100644 index 0000000..d41b467 --- /dev/null +++ b/validate/gst/validate/gst-validate-media-info.h @@ -0,0 +1,80 @@ +/* GStreamer + * Copyright (C) 2013 Thiago Santos + * + * gst-validate-media-info.h - Media information structure + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_VALIDATE_MEDIA_INFO_H__ +#define __GST_VALIDATE_MEDIA_INFO_H__ + +#include +#include + +G_BEGIN_DECLS + +typedef struct _GstValidateMediaInfo GstValidateMediaInfo; +typedef struct _GstValidateStreamInfo GstValidateStreamInfo; + +/** + * GstValidateMediaInfo: + * + * GStreamer Validate MediaInfo struct. + * + * Stores extracted information about a media + */ +struct _GstValidateMediaInfo { + + /* */ + /* Value for the expected total duration of the file in nanosecs + * Set to GST_CLOCK_TIME_NONE if it shouldn't be tested */ + GstClockTime duration; + gboolean is_image; + + /* Expected file_size, set to 0 to skip test */ + guint64 file_size; + + gboolean seekable; + + gchar *playback_error; + gchar *reverse_playback_error; + gchar *track_switch_error; + + gchar *uri; + + gboolean discover_only; + + GstValidateStreamInfo *stream_info; +}; + +void gst_validate_media_info_init (GstValidateMediaInfo * mi); +void gst_validate_media_info_clear (GstValidateMediaInfo * mi); +void gst_validate_media_info_free (GstValidateMediaInfo * mi); + +gchar * gst_validate_media_info_to_string (GstValidateMediaInfo * mi, gsize * length); +gboolean gst_validate_media_info_save (GstValidateMediaInfo * mi, const gchar * path, GError ** err); +GstValidateMediaInfo * gst_validate_media_info_load (const gchar * path, GError ** err); + +gboolean gst_validate_media_info_inspect_uri (GstValidateMediaInfo * mi, const gchar * uri, + gboolean discover_only, GError ** err); + +gboolean gst_validate_media_info_compare (GstValidateMediaInfo * expected, GstValidateMediaInfo * extracted); + +G_END_DECLS + +#endif /* __GST_VALIDATE_MEDIA_INFO_H__ */ + diff --git a/validate/gst/validate/gst-validate-monitor-factory.c b/validate/gst/validate/gst-validate-monitor-factory.c new file mode 100644 index 0000000..cc3cb65 --- /dev/null +++ b/validate/gst/validate/gst-validate-monitor-factory.c @@ -0,0 +1,80 @@ +/* GStreamer + * + * Copyright (C) 2013 Collabora Ltd. + * Author: Thiago Sousa Santos + * + * gst-validate-monitor-factory.c - Validate Element monitors factory utility functions + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:gst-validate-monitor-factory + * @short_description: Lets you start monitoring a #GstObject with GstValidate + * + * To start monitoring and thus run GstValidate tests on a #GstPipeline, the only thing to + * do is to instanciate a #GstValidateRunner and then attach a #GstValidateMonitor + * to it with #gst_validate_monitor_factory_create + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "gst-validate-monitor-factory.h" +#include "gst-validate-bin-monitor.h" +#include "gst-validate-pipeline-monitor.h" +#include "gst-validate-pad-monitor.h" +#include "gst-validate-override-registry.h" + +/** + * gst_validate_monitor_factory_create: + * @target: The #GstObject to create a #GstValidateMonitor for + * @runner: The #GstValidateRunner to use for the new monitor + * @parent: (optional) (nullable): The parent of the new monitor + * + * Create a new monitor for @target and starts monitoring it. + * + * Returns: (transfer full): The newly created #GstValidateMonitor + */ +GstValidateMonitor * +gst_validate_monitor_factory_create (GstObject * target, + GstValidateRunner * runner, GstValidateMonitor * parent) +{ + GstValidateMonitor *monitor = NULL; + g_return_val_if_fail (target != NULL, NULL); + + if (GST_IS_PAD (target)) { + monitor = + GST_VALIDATE_MONITOR_CAST (gst_validate_pad_monitor_new (GST_PAD_CAST + (target), runner, GST_VALIDATE_ELEMENT_MONITOR_CAST (parent))); + } else if (GST_IS_PIPELINE (target)) { + monitor = + GST_VALIDATE_MONITOR_CAST (gst_validate_pipeline_monitor_new + (GST_PIPELINE_CAST (target), runner, parent)); + } else if (GST_IS_BIN (target)) { + monitor = + GST_VALIDATE_MONITOR_CAST (gst_validate_bin_monitor_new (GST_BIN_CAST + (target), runner, parent)); + } else if (GST_IS_ELEMENT (target)) { + monitor = + GST_VALIDATE_MONITOR_CAST (gst_validate_element_monitor_new + (GST_ELEMENT_CAST (target), runner, parent)); + } + + gst_validate_override_registry_attach_overrides (monitor); + return monitor; +} diff --git a/validate/gst/validate/gst-validate-monitor-factory.h b/validate/gst/validate/gst-validate-monitor-factory.h new file mode 100644 index 0000000..d2d6bcf --- /dev/null +++ b/validate/gst/validate/gst-validate-monitor-factory.h @@ -0,0 +1,38 @@ +/* GStreamer + * Copyright (C) 2013 Thiago Santos + * + * gst-validate-monitor-factory.h - Validate Element monitors factory utility functions + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_VALIDATE_MONITOR_FACTORY_H__ +#define __GST_VALIDATE_MONITOR_FACTORY_H__ + +#include +#include + +#include +#include + +G_BEGIN_DECLS + +GstValidateMonitor * gst_validate_monitor_factory_create (GstObject * target, GstValidateRunner * runner, GstValidateMonitor * parent); + +G_END_DECLS + +#endif /* __GST_VALIDATE_MONITOR_FACTORY_H__ */ + diff --git a/validate/gst/validate/gst-validate-monitor.c b/validate/gst/validate/gst-validate-monitor.c new file mode 100644 index 0000000..03e9c26 --- /dev/null +++ b/validate/gst/validate/gst-validate-monitor.c @@ -0,0 +1,413 @@ +/* GStreamer + * + * Copyright (C) 2013 Collabora Ltd. + * Author: Thiago Sousa Santos + * + * gst-validate-monitor.c - Validate Monitor class + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "gst-validate-internal.h" +#include "gst-validate-monitor.h" + +/** + * SECTION:gst-validate-monitor + * @short_description: Base class that wraps a #GObject for Validate checks + * + * TODO + */ + +enum +{ + PROP_0, + PROP_OBJECT, + PROP_RUNNER, + PROP_VALIDATE_PARENT, + PROP_LAST +}; + +static gboolean gst_validate_monitor_do_setup (GstValidateMonitor * monitor); +static void +gst_validate_monitor_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static void +gst_validate_monitor_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static GObject *gst_validate_monitor_constructor (GType type, + guint n_construct_params, GObjectConstructParam * construct_params); + +gboolean gst_validate_monitor_setup (GstValidateMonitor * monitor); + +static GstValidateInterceptionReturn +gst_validate_monitor_intercept_report (GstValidateReporter * reporter, + GstValidateReport * report); + +#define _do_init \ + G_IMPLEMENT_INTERFACE (GST_TYPE_VALIDATE_REPORTER, _reporter_iface_init) + +static GstValidateReportingDetails +_get_reporting_level (GstValidateReporter * monitor) +{ + return GST_VALIDATE_MONITOR (monitor)->level; +} + +static void +_reporter_iface_init (GstValidateReporterInterface * iface) +{ + iface->intercept_report = gst_validate_monitor_intercept_report; + iface->get_reporting_level = _get_reporting_level; +} + +#define gst_validate_monitor_parent_class parent_class +G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstValidateMonitor, gst_validate_monitor, + G_TYPE_OBJECT, _do_init); + +static void +_target_freed_cb (GstValidateMonitor * monitor, GObject * where_the_object_was) +{ + GST_DEBUG_OBJECT (monitor, "Target was freed"); + monitor->target = NULL; +} + +static void +gst_validate_monitor_dispose (GObject * object) +{ + GstValidateMonitor *monitor = GST_VALIDATE_MONITOR_CAST (object); + + g_mutex_clear (&monitor->mutex); + g_mutex_clear (&monitor->overrides_mutex); + g_queue_clear (&monitor->overrides); + + if (monitor->target) + g_object_weak_unref (G_OBJECT (monitor->target), + (GWeakNotify) _target_freed_cb, monitor); + + if (monitor->media_descriptor) + gst_object_unref (monitor->media_descriptor); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gst_validate_monitor_finalize (GObject * object) +{ + gst_validate_reporter_set_name (GST_VALIDATE_REPORTER (object), NULL); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_validate_monitor_class_init (GstValidateMonitorClass * klass) +{ + GObjectClass *gobject_class; + + gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->get_property = gst_validate_monitor_get_property; + gobject_class->set_property = gst_validate_monitor_set_property; + gobject_class->dispose = gst_validate_monitor_dispose; + gobject_class->finalize = gst_validate_monitor_finalize; + gobject_class->constructor = gst_validate_monitor_constructor; + + klass->setup = gst_validate_monitor_do_setup; + + g_object_class_install_property (gobject_class, PROP_OBJECT, + g_param_spec_object ("object", "Object", "The object to be monitored", + G_TYPE_OBJECT, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_RUNNER, + g_param_spec_object ("validate-runner", "VALIDATE Runner", + "The Validate runner to " "report errors to", + GST_TYPE_VALIDATE_RUNNER, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_VALIDATE_PARENT, + g_param_spec_object ("validate-parent", "VALIDATE parent monitor", + "The Validate monitor " "that is the parent of this one", + GST_TYPE_VALIDATE_MONITOR, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); +} + +static GObject * +gst_validate_monitor_constructor (GType type, guint n_construct_params, + GObjectConstructParam * construct_params) +{ + GstValidateMonitor *monitor = + GST_VALIDATE_MONITOR_CAST (G_OBJECT_CLASS (parent_class)->constructor + (type, + n_construct_params, + construct_params)); + + if (monitor->parent) { + gst_validate_monitor_set_media_descriptor (monitor, + monitor->parent->media_descriptor); + } + + + gst_validate_monitor_setup (monitor); + return (GObject *) monitor; +} + +static void +gst_validate_monitor_init (GstValidateMonitor * monitor) +{ + g_mutex_init (&monitor->mutex); + + g_mutex_init (&monitor->overrides_mutex); + g_queue_init (&monitor->overrides); +} + +#if 0 +/* This shouldn't be used. it's a base class */ +/** + * gst_validate_monitor_new: + * @element: (transfer-none): a #GObject to run Validate on + */ +GstValidateMonitor * +gst_validate_monitor_new (GObject * object) +{ + GstValidateMonitor *monitor = + g_object_new (GST_TYPE_VALIDATE_MONITOR, "object", + G_TYPE_OBJECT, object, NULL); + + if (GST_VALIDATE_MONITOR_GET_OBJECT (monitor) == NULL) { + /* setup failed, no use on returning this monitor */ + g_object_unref (monitor); + return NULL; + } + + return monitor; +} +#endif + +static gboolean +gst_validate_monitor_do_setup (GstValidateMonitor * monitor) +{ + /* NOP */ + return TRUE; +} + +static GstValidateReportingDetails +_get_report_level_for_pad (GstValidateRunner * runner, GstObject * pad) +{ + GstObject *parent; + gchar *name; + GstValidateReportingDetails level = GST_VALIDATE_SHOW_UNKNOWN; + + parent = gst_object_get_parent (pad); + + name = g_strdup_printf ("%s__%s", GST_DEBUG_PAD_NAME (pad)); + level = gst_validate_runner_get_reporting_level_for_name (runner, name); + + g_free (name); + gst_object_unref (parent); + return level; +} + +static void +_determine_reporting_level (GstValidateMonitor * monitor) +{ + GstValidateRunner *runner; + GstObject *object, *parent; + gchar *object_name; + GstValidateReportingDetails level = GST_VALIDATE_SHOW_UNKNOWN; + + object = gst_object_ref (monitor->target); + runner = gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER (monitor)); + + do { + if (!GST_IS_OBJECT (object)) + break; + + /* Let's allow for singling out pads */ + if (GST_IS_PAD (object)) { + level = _get_report_level_for_pad (runner, object); + if (level != GST_VALIDATE_SHOW_UNKNOWN) + break; + } + + object_name = gst_object_get_name (object); + level = + gst_validate_runner_get_reporting_level_for_name (runner, object_name); + parent = gst_object_get_parent (object); + gst_object_unref (object); + object = parent; + g_free (object_name); + } while (object && level == GST_VALIDATE_SHOW_UNKNOWN); + + if (object) + gst_object_unref (object); + + monitor->level = level; +} + +gboolean +gst_validate_monitor_setup (GstValidateMonitor * monitor) +{ + GST_DEBUG_OBJECT (monitor, "Starting monitor setup"); + + /* For now we just need to do this at setup time */ + _determine_reporting_level (monitor); + return GST_VALIDATE_MONITOR_GET_CLASS (monitor)->setup (monitor); +} + +GstElement * +gst_validate_monitor_get_element (GstValidateMonitor * monitor) +{ + GstValidateMonitorClass *klass = GST_VALIDATE_MONITOR_GET_CLASS (monitor); + GstElement *element = NULL; + + if (klass->get_element) + element = klass->get_element (monitor); + + return element; +} + +const gchar * +gst_validate_monitor_get_element_name (GstValidateMonitor * monitor) +{ + GstElement *element; + + element = gst_validate_monitor_get_element (monitor); + if (element) + return GST_ELEMENT_NAME (element); + return NULL; +} + +/* Check if any of our overrides wants to change the report severity */ +static GstValidateInterceptionReturn +gst_validate_monitor_intercept_report (GstValidateReporter * reporter, + GstValidateReport * report) +{ + GList *iter; + GstValidateMonitor *monitor = GST_VALIDATE_MONITOR_CAST (reporter); + + GST_VALIDATE_MONITOR_OVERRIDES_LOCK (monitor); + for (iter = monitor->overrides.head; iter; iter = g_list_next (iter)) { + report->level = + gst_validate_override_get_severity (iter->data, + gst_validate_issue_get_id (report->issue), report->level); + } + GST_VALIDATE_MONITOR_OVERRIDES_UNLOCK (monitor); + + return GST_VALIDATE_REPORTER_REPORT; +} + +void +gst_validate_monitor_attach_override (GstValidateMonitor * monitor, + GstValidateOverride * override) +{ + GstValidateRunner *runner; + + if (!gst_validate_override_can_attach (override, monitor)) { + GST_INFO_OBJECT (monitor, "Can not attach override %s", + gst_validate_reporter_get_name (GST_VALIDATE_REPORTER (override))); + + return; + } + + runner = gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER (override)); + + GST_VALIDATE_MONITOR_OVERRIDES_LOCK (monitor); + if (runner) + g_assert (runner == + gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER (monitor))); + else + gst_validate_reporter_set_runner (GST_VALIDATE_REPORTER (override), + gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER (monitor))); + g_queue_push_tail (&monitor->overrides, override); + GST_VALIDATE_MONITOR_OVERRIDES_UNLOCK (monitor); +} + +static void +gst_validate_monitor_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstValidateMonitor *monitor; + + monitor = GST_VALIDATE_MONITOR_CAST (object); + + switch (prop_id) { + case PROP_OBJECT: + g_assert (monitor->target == NULL); + monitor->target = g_value_get_object (value); + g_object_weak_ref (G_OBJECT (monitor->target), + (GWeakNotify) _target_freed_cb, monitor); + + if (monitor->target) + gst_validate_reporter_set_name (GST_VALIDATE_REPORTER (monitor), + g_strdup (GST_OBJECT_NAME (monitor->target))); + break; + case PROP_RUNNER: + gst_validate_reporter_set_runner (GST_VALIDATE_REPORTER (monitor), + g_value_get_object (value)); + break; + case PROP_VALIDATE_PARENT: + monitor->parent = g_value_get_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_validate_monitor_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstValidateMonitor *monitor; + + monitor = GST_VALIDATE_MONITOR_CAST (object); + + switch (prop_id) { + case PROP_OBJECT: + g_value_set_object (value, GST_VALIDATE_MONITOR_GET_OBJECT (monitor)); + break; + case PROP_RUNNER: + g_value_set_object (value, GST_VALIDATE_MONITOR_GET_RUNNER (monitor)); + break; + case PROP_VALIDATE_PARENT: + g_value_set_object (value, GST_VALIDATE_MONITOR_GET_PARENT (monitor)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +void +gst_validate_monitor_set_media_descriptor (GstValidateMonitor * monitor, + GstMediaDescriptor * media_descriptor) +{ + GstValidateMonitorClass *klass = GST_VALIDATE_MONITOR_GET_CLASS (monitor); + + GST_DEBUG_OBJECT (monitor->target, "Set media desc: %" GST_PTR_FORMAT, + media_descriptor); + if (monitor->media_descriptor) + gst_object_unref (monitor->media_descriptor); + + if (media_descriptor) + gst_object_ref (media_descriptor); + + monitor->media_descriptor = media_descriptor; + if (klass->set_media_descriptor) + klass->set_media_descriptor (monitor, media_descriptor); +} diff --git a/validate/gst/validate/gst-validate-monitor.h b/validate/gst/validate/gst-validate-monitor.h new file mode 100644 index 0000000..0a6f4cd --- /dev/null +++ b/validate/gst/validate/gst-validate-monitor.h @@ -0,0 +1,130 @@ +/* GStreamer + * Copyright (C) 2013 Thiago Santos + * + * gst-validate-monitor.h - Validate Monitor abstract base class + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_VALIDATE_MONITOR_H__ +#define __GST_VALIDATE_MONITOR_H__ + +#include +#include + +typedef struct _GstValidateMonitor GstValidateMonitor; +typedef struct _GstValidateMonitorClass GstValidateMonitorClass; + +#include +#include +#include +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_VALIDATE_MONITOR (gst_validate_monitor_get_type ()) +#define GST_IS_VALIDATE_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_VALIDATE_MONITOR)) +#define GST_IS_VALIDATE_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_VALIDATE_MONITOR)) +#define GST_VALIDATE_MONITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_VALIDATE_MONITOR, GstValidateMonitorClass)) +#define GST_VALIDATE_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_VALIDATE_MONITOR, GstValidateMonitor)) +#define GST_VALIDATE_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_VALIDATE_MONITOR, GstValidateMonitorClass)) +#define GST_VALIDATE_MONITOR_CAST(obj) ((GstValidateMonitor*)(obj)) +#define GST_VALIDATE_MONITOR_CLASS_CAST(klass) ((GstValidateMonitorClass*)(klass)) + +#define GST_VALIDATE_MONITOR_GET_OBJECT(m) (GST_VALIDATE_MONITOR_CAST (m)->target) +#define GST_VALIDATE_MONITOR_GET_RUNNER(m) (gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER_CAST (m))) +#define GST_VALIDATE_MONITOR_GET_PARENT(m) (GST_VALIDATE_MONITOR_CAST (m)->parent) + +#define GST_VALIDATE_MONITOR_LOCK(m) \ + G_STMT_START { \ + GST_LOG_OBJECT (m, "About to lock %p", &GST_VALIDATE_MONITOR_CAST(m)->mutex); \ + (g_mutex_lock (&GST_VALIDATE_MONITOR_CAST(m)->mutex)); \ + GST_LOG_OBJECT (m, "Acquired lock %p", &GST_VALIDATE_MONITOR_CAST(m)->mutex); \ + } G_STMT_END + +#define GST_VALIDATE_MONITOR_UNLOCK(m) \ + G_STMT_START { \ + GST_LOG_OBJECT (m, "About to unlock %p", &GST_VALIDATE_MONITOR_CAST(m)->mutex); \ + (g_mutex_unlock (&GST_VALIDATE_MONITOR_CAST(m)->mutex)); \ + GST_LOG_OBJECT (m, "unlocked %p", &GST_VALIDATE_MONITOR_CAST(m)->mutex); \ + } G_STMT_END + +#define GST_VALIDATE_MONITOR_OVERRIDES_LOCK(m) g_mutex_lock (&GST_VALIDATE_MONITOR_CAST (m)->overrides_mutex) +#define GST_VALIDATE_MONITOR_OVERRIDES_UNLOCK(m) g_mutex_unlock (&GST_VALIDATE_MONITOR_CAST (m)->overrides_mutex) +#define GST_VALIDATE_MONITOR_OVERRIDES(m) (GST_VALIDATE_MONITOR_CAST (m)->overrides) + +/* #else TODO Implemen no variadic macros, use inline, + * Problem being: + * GST_VALIDATE_REPORT_LEVEL_ ## status + * GST_VALIDATE_AREA_ ## area ## _ ## subarea + */ + +/** + * GstValidateMonitor: + * + * GStreamer Validate Monitor class. + * + * Class that wraps a #GObject for Validate checks + */ +struct _GstValidateMonitor { + GObject object; + + GstObject *target; + GMutex mutex; + gchar *target_name; + + GstValidateMonitor *parent; + + GMutex overrides_mutex; + GQueue overrides; + GstMediaDescriptor *media_descriptor; + + GstValidateReportingDetails level; + + /*< private >*/ + GHashTable *reports; +}; + +/** + * GstValidateMonitorClass: + * @parent_class: parent + * + * GStreamer Validate Monitor object class. + */ +struct _GstValidateMonitorClass { + GObjectClass parent_class; + + gboolean (* setup) (GstValidateMonitor * monitor); + GstElement *(* get_element) (GstValidateMonitor * monitor); + void (*set_media_descriptor) (GstValidateMonitor * monitor, + GstMediaDescriptor * media_descriptor); +}; + +/* normal GObject stuff */ +GType gst_validate_monitor_get_type (void); + +void gst_validate_monitor_attach_override (GstValidateMonitor * monitor, + GstValidateOverride * override); + +GstElement * gst_validate_monitor_get_element (GstValidateMonitor * monitor); +const gchar * gst_validate_monitor_get_element_name (GstValidateMonitor * monitor); +void gst_validate_monitor_set_media_descriptor (GstValidateMonitor * monitor, + GstMediaDescriptor *media_descriptor); +G_END_DECLS + +#endif /* __GST_VALIDATE_MONITOR_H__ */ + diff --git a/validate/gst/validate/gst-validate-override-registry.c b/validate/gst/validate/gst-validate-override-registry.c new file mode 100644 index 0000000..b68a47f --- /dev/null +++ b/validate/gst/validate/gst-validate-override-registry.c @@ -0,0 +1,463 @@ +/* GStreamer + * + * Copyright (C) 2013 Collabora Ltd. + * Author: Thiago Sousa Santos + * + * gst-validate-override-registry.c - Validate Override Registry + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include + +#include "gst-validate-report.h" +#include "gst-validate-utils.h" +#include "gst-validate-internal.h" +#include "gst-validate-monitor.h" +#include "gst-validate-override.h" +#include "gst-validate-override-registry.h" + +typedef struct +{ + gchar *name; + GstValidateOverride *override; +} GstValidateOverrideRegistryNameEntry; + +typedef struct +{ + GType gtype; + GstValidateOverride *override; +} GstValidateOverrideRegistryGTypeEntry; + +static GMutex _gst_validate_override_registry_mutex; +static GstValidateOverrideRegistry *_registry_default = NULL; + +#define GST_VALIDATE_OVERRIDE_REGISTRY_LOCK(r) g_mutex_lock (&r->mutex) +#define GST_VALIDATE_OVERRIDE_REGISTRY_UNLOCK(r) g_mutex_unlock (&r->mutex) + +#define GST_VALIDATE_OVERRIDE_INIT_SYMBOL "gst_validate_create_overrides" +typedef int (*GstValidateCreateOverride) (void); + +static void + gst_validate_override_registry_name_entry_free + (GstValidateOverrideRegistryNameEntry * entry) +{ + g_free (entry->name); + gst_object_unref (entry->override); + + g_slice_free (GstValidateOverrideRegistryNameEntry, entry); +} + +static void + gst_validate_override_registry_type_entry_free + (GstValidateOverrideRegistryGTypeEntry * entry) +{ + gst_object_unref (entry->override); + + g_slice_free (GstValidateOverrideRegistryGTypeEntry, entry); +} + +static GstValidateOverrideRegistry * +gst_validate_override_registry_new (void) +{ + GstValidateOverrideRegistry *reg = g_slice_new0 (GstValidateOverrideRegistry); + + g_mutex_init (®->mutex); + g_queue_init (®->name_overrides); + g_queue_init (®->gtype_overrides); + g_queue_init (®->klass_overrides); + + return reg; +} + +static void +gst_validate_overide_registery_free (GstValidateOverrideRegistry * reg) +{ + g_queue_foreach (®->klass_overrides, + (GFunc) gst_validate_override_registry_name_entry_free, NULL); + + g_queue_foreach (®->name_overrides, + (GFunc) gst_validate_override_registry_name_entry_free, NULL); + + g_queue_foreach (®->gtype_overrides, + (GFunc) gst_validate_override_registry_type_entry_free, NULL); + + g_queue_clear (®->name_overrides); + g_queue_clear (®->gtype_overrides); + g_queue_clear (®->klass_overrides); + + g_slice_free (GstValidateOverrideRegistry, reg); +} + +GstValidateOverrideRegistry * +gst_validate_override_registry_get (void) +{ + g_mutex_lock (&_gst_validate_override_registry_mutex); + if (G_UNLIKELY (!_registry_default)) { + _registry_default = gst_validate_override_registry_new (); + } + g_mutex_unlock (&_gst_validate_override_registry_mutex); + + return _registry_default; +} + +void +gst_validate_override_register_by_name (const gchar * name, + GstValidateOverride * override) +{ + GstValidateOverrideRegistry *registry = gst_validate_override_registry_get (); + GstValidateOverrideRegistryNameEntry *entry = + g_slice_new (GstValidateOverrideRegistryNameEntry); + + GST_VALIDATE_OVERRIDE_REGISTRY_LOCK (registry); + entry->name = g_strdup (name); + entry->override = override; + g_queue_push_tail (®istry->name_overrides, entry); + GST_VALIDATE_OVERRIDE_REGISTRY_UNLOCK (registry); +} + +void +gst_validate_override_register_by_type (GType gtype, + GstValidateOverride * override) +{ + GstValidateOverrideRegistry *registry = gst_validate_override_registry_get (); + GstValidateOverrideRegistryGTypeEntry *entry = + g_slice_new (GstValidateOverrideRegistryGTypeEntry); + + GST_VALIDATE_OVERRIDE_REGISTRY_LOCK (registry); + entry->gtype = gtype; + entry->override = override; + g_queue_push_tail (®istry->gtype_overrides, entry); + GST_VALIDATE_OVERRIDE_REGISTRY_UNLOCK (registry); +} + +void +gst_validate_override_register_by_klass (const gchar * klass, + GstValidateOverride * override) +{ + GstValidateOverrideRegistry *registry = gst_validate_override_registry_get (); + GstValidateOverrideRegistryNameEntry *entry = + g_slice_new (GstValidateOverrideRegistryNameEntry); + + GST_VALIDATE_OVERRIDE_REGISTRY_LOCK (registry); + entry->name = g_strdup (klass); + entry->override = override; + g_queue_push_tail (®istry->klass_overrides, entry); + GST_VALIDATE_OVERRIDE_REGISTRY_UNLOCK (registry); +} + +static void + gst_validate_override_registry_attach_name_overrides_unlocked + (GstValidateOverrideRegistry * registry, GstValidateMonitor * monitor) +{ + GstValidateOverrideRegistryNameEntry *entry; + GList *iter; + const gchar *name; + + name = gst_validate_monitor_get_element_name (monitor); + for (iter = registry->name_overrides.head; iter; iter = g_list_next (iter)) { + entry = iter->data; + if (g_regex_match_simple (entry->name, name, 0, 0)) { + GST_INFO_OBJECT (registry, "Adding override %s to %s", entry->name, name); + + gst_validate_monitor_attach_override (monitor, entry->override); + } + } +} + +static void + gst_validate_override_registry_attach_gtype_overrides_unlocked + (GstValidateOverrideRegistry * registry, GstValidateMonitor * monitor) +{ + GstValidateOverrideRegistryGTypeEntry *entry; + GstElement *element; + GList *iter; + + element = gst_validate_monitor_get_element (monitor); + if (!element) + return; + + for (iter = registry->gtype_overrides.head; iter; iter = g_list_next (iter)) { + entry = iter->data; + if (G_TYPE_CHECK_INSTANCE_TYPE (element, entry->gtype)) { + gst_validate_monitor_attach_override (monitor, entry->override); + } + } +} + +static void + gst_validate_override_registry_attach_klass_overrides_unlocked + (GstValidateOverrideRegistry * registry, GstValidateMonitor * monitor) +{ + GstValidateOverrideRegistryNameEntry *entry; + GList *iter; + GstElement *element; + + element = gst_validate_monitor_get_element (monitor); + if (!element) + return; + + for (iter = registry->klass_overrides.head; iter; iter = g_list_next (iter)) { + + entry = iter->data; + + if (gst_validate_element_has_klass (element, entry->name)) { + gst_validate_monitor_attach_override (monitor, entry->override); + } + } +} + +void +gst_validate_override_registry_attach_overrides (GstValidateMonitor * monitor) +{ + GstValidateOverrideRegistry *reg = gst_validate_override_registry_get (); + + GST_VALIDATE_OVERRIDE_REGISTRY_LOCK (reg); + gst_validate_override_registry_attach_name_overrides_unlocked (reg, monitor); + gst_validate_override_registry_attach_gtype_overrides_unlocked (reg, monitor); + gst_validate_override_registry_attach_klass_overrides_unlocked (reg, monitor); + GST_VALIDATE_OVERRIDE_REGISTRY_UNLOCK (reg); +} + +enum +{ + WRONG_FILE, + WRONG_OVERRIDES, + OK +}; + +static gboolean +_add_override_from_struct (GstStructure * soverride) +{ + GQuark issue_id; + GstValidateReportLevel level; + GstValidateOverride *override; + const gchar *str_issue_id, *str_new_severity, + *factory_name = NULL, *name = NULL, *klass = NULL; + + gboolean registered = FALSE; + + if (!gst_structure_has_name (soverride, "change-severity")) { + GST_ERROR ("Currently only 'change-severity' overrides are supported"); + + return FALSE; + } + + str_issue_id = gst_structure_get_string (soverride, "issue-id"); + if (!str_issue_id) { + GST_ERROR ("No issue id provided in override: %" GST_PTR_FORMAT, soverride); + + return FALSE; + } + + issue_id = g_quark_from_string (str_issue_id); + if (gst_validate_issue_from_id (issue_id) == NULL) { + GST_ERROR ("No GstValidateIssue registered for %s", str_issue_id); + + return FALSE; + } + + str_new_severity = gst_structure_get_string (soverride, "new-severity"); + if (str_new_severity == NULL) { + GST_ERROR ("No 'new-severity' field found in %" GST_PTR_FORMAT, soverride); + + return FALSE; + } + + level = gst_validate_report_level_from_name (str_new_severity); + if (level == GST_VALIDATE_REPORT_LEVEL_UNKNOWN) { + GST_ERROR ("Unknown level name %s", str_new_severity); + + return FALSE; + } + + override = gst_validate_override_new (); + gst_validate_override_change_severity (override, issue_id, level); + + name = gst_structure_get_string (soverride, "element-name"); + klass = gst_structure_get_string (soverride, "element-classification"); + factory_name = gst_structure_get_string (soverride, "element-factory-name"); + + if (factory_name) { + GType type; + GstElement *element = gst_element_factory_make (factory_name, NULL); + + if (element == NULL) { + GST_ERROR ("Unknown element factory name: %s (gst is %sinitialized)", + factory_name, gst_is_initialized ()? "" : "NOT "); + + if (!name && !klass) + return FALSE; + } else { + type = G_OBJECT_TYPE (element); + gst_validate_override_register_by_type (type, override); + } + + registered = TRUE; + } + + if (name) { + gst_validate_override_register_by_name (name, override); + registered = TRUE; + } + + if (klass) { + gst_validate_override_register_by_klass (klass, override); + registered = TRUE; + } + + if (!registered) { + GstValidateIssue *issue = gst_validate_issue_from_id (issue_id); + + if (!issue) { + + return FALSE; + } + + gst_validate_issue_set_default_level (issue, level); + } + + return TRUE; +} + +static gboolean +_load_text_override_file (const gchar * filename) +{ + gint ret = OK; + GList *structs = gst_validate_utils_structs_parse_from_filename (filename); + + if (structs) { + GList *tmp; + + for (tmp = structs; tmp; tmp = tmp->next) { + if (!_add_override_from_struct (tmp->data)) { + GST_ERROR ("Wrong overrides %" GST_PTR_FORMAT, tmp->data); + ret = WRONG_OVERRIDES; + } + } + + goto done; + } + + ret = WRONG_FILE; + +done: + g_list_free_full (structs, (GDestroyNotify) gst_structure_free); + return ret; +} + +int +gst_validate_override_registry_preload (void) +{ + gchar **modlist, *const *modname; + const char *sos, *loaderr; + GModule *module; + int ret, nloaded = 0; + gpointer ext_create_overrides; + + sos = g_getenv ("GST_VALIDATE_OVERRIDE"); + if (!sos) { + GST_INFO ("No GST_VALIDATE_OVERRIDE found, no overrides to load"); + return 0; + } + + modlist = g_strsplit (sos, G_SEARCHPATH_SEPARATOR_S, 0); + for (modname = modlist; *modname; ++modname) { + GST_INFO ("Loading overrides from %s", *modname); + module = g_module_open (*modname, G_MODULE_BIND_LAZY); + if (module == NULL) { + + if (_load_text_override_file (*modname) == WRONG_FILE) { + loaderr = g_module_error (); + GST_ERROR ("Failed to load %s %s", *modname, + loaderr ? loaderr : "no idea why"); + } + + continue; + } + if (g_module_symbol (module, GST_VALIDATE_OVERRIDE_INIT_SYMBOL, + &ext_create_overrides)) { + ret = ((GstValidateCreateOverride) ext_create_overrides) (); + if (ret > 0) { + GST_INFO ("Loaded %d overrides from %s", ret, *modname); + nloaded += ret; + } else if (ret < 0) { + GST_WARNING ("Error loading overrides from %s", *modname); + } else { + GST_INFO ("Loaded no overrides from %s", *modname); + } + } else { + GST_WARNING (GST_VALIDATE_OVERRIDE_INIT_SYMBOL " not found in %s", + *modname); + } + g_module_close (module); + } + g_strfreev (modlist); + GST_INFO ("%d overrides loaded", nloaded); + return nloaded; +} + +GList *gst_validate_override_registry_get_override_for_names + (GstValidateOverrideRegistry * reg, const gchar * name, ...) +{ + GList *iter; + GList *ret = NULL; + + if (name) { + va_list varargs; + GstValidateOverrideRegistryNameEntry *entry; + + va_start (varargs, name); + + GST_VALIDATE_OVERRIDE_REGISTRY_LOCK (reg); + while (name) { + for (iter = reg->name_overrides.head; iter; iter = g_list_next (iter)) { + entry = iter->data; + if ((g_strcmp0 (name, entry->name)) == 0) { + ret = g_list_prepend (ret, entry->override); + } + } + name = va_arg (varargs, gchar *); + } + GST_VALIDATE_OVERRIDE_REGISTRY_UNLOCK (reg); + + va_end (varargs); + } + + return ret; +} + +void +_priv_validate_override_registry_deinit (void) +{ + GstValidateOverrideRegistry *reg = NULL; + + g_mutex_lock (&_gst_validate_override_registry_mutex); + if (G_UNLIKELY (_registry_default)) { + reg = _registry_default; + _registry_default = NULL; + } + g_mutex_unlock (&_gst_validate_override_registry_mutex); + + if (reg) + gst_validate_overide_registery_free (reg); +} diff --git a/validate/gst/validate/gst-validate-override-registry.h b/validate/gst/validate/gst-validate-override-registry.h new file mode 100644 index 0000000..1b8543d --- /dev/null +++ b/validate/gst/validate/gst-validate-override-registry.h @@ -0,0 +1,58 @@ +/* GStreamer + * Copyright (C) 2013 Thiago Santos + * + * gst-validate-override-registry.h - Validate Override registry + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_VALIDATE_OVERRIDE_REGISTRY_H__ +#define __GST_VALIDATE_OVERRIDE_REGISTRY_H__ + +#include +#include + +#include +#include +#include + +G_BEGIN_DECLS + +typedef struct { + GMutex mutex; + + GQueue name_overrides; + GQueue gtype_overrides; + GQueue klass_overrides; +} GstValidateOverrideRegistry; + +GstValidateOverrideRegistry * gst_validate_override_registry_get (void); + +GList * +gst_validate_override_registry_get_override_for_names (GstValidateOverrideRegistry *reg, + const gchar *name, ...); +void gst_validate_override_register_by_name (const gchar * name, GstValidateOverride * override); +void gst_validate_override_register_by_type (GType gtype, GstValidateOverride * override); +void gst_validate_override_register_by_klass (const gchar * klass, GstValidateOverride * override); + +void gst_validate_override_registry_attach_overrides (GstValidateMonitor * monitor); + +int gst_validate_override_registry_preload (void); + +G_END_DECLS + +#endif /* __GST_VALIDATE_OVERRIDE_REGISTRY_H__ */ + diff --git a/validate/gst/validate/gst-validate-override.c b/validate/gst/validate/gst-validate-override.c new file mode 100644 index 0000000..875ffd9 --- /dev/null +++ b/validate/gst/validate/gst-validate-override.c @@ -0,0 +1,265 @@ +/* GStreamer + * + * Copyright (C) 2013 Collabora Ltd. + * Author: Thiago Sousa Santos + * Copyright (C) 2015 Raspberry Pi Foundation + * Author: Thibault Saunier + * + * gst-validate-override.c - Validate Override that allows customizing Validate behavior + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/** + * SECTION: gst-validate-override + * @title: GstValidateOverride + * @short_description: TODO + * + * TODO + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "gst-validate-internal.h" +#include "gst-validate-override.h" + +/* *INDENT-OFF* */ + +G_DEFINE_TYPE_WITH_CODE (GstValidateOverride, gst_validate_override, + G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (GST_TYPE_VALIDATE_REPORTER, NULL)) + +struct _GstValidateOverridePriv +{ + GHashTable *level_override; +}; + +enum +{ + PROP_FIRST_PROP = 1, + PROP_RUNNER, + PROP_LAST +}; + +/* *INDENT-ON* */ + +static void +_get_property (GObject * object, + guint property_id, GValue * value, GParamSpec * pspec) +{ + switch (property_id) { + case PROP_RUNNER: + g_value_set_object (value, + gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER (object))); + break; + default: + break; + } +} + +static void +_set_property (GObject * object, + guint property_id, const GValue * value, GParamSpec * pspec) +{ + switch (property_id) { + case PROP_RUNNER: + /* we assume the runner is valid as long as this scenario is, + * no ref taken */ + gst_validate_reporter_set_runner (GST_VALIDATE_REPORTER (object), + g_value_get_object (value)); + break; + default: + break; + } +} + +static void +gst_validate_override_finalize (GObject * object) +{ + GstValidateOverride *self = GST_VALIDATE_OVERRIDE (object); + + void (*chain_up) (GObject *) = + ((GObjectClass *) gst_validate_override_parent_class)->finalize; + + g_hash_table_unref (self->priv->level_override); + + chain_up (object); +} + +static void +gst_validate_override_class_init (GstValidateOverrideClass * klass) +{ + GObjectClass *oclass = G_OBJECT_CLASS (klass); + + oclass->finalize = gst_validate_override_finalize; + + g_type_class_add_private (klass, sizeof (GstValidateOverridePriv)); + + oclass->get_property = _get_property; + oclass->set_property = _set_property; + + g_object_class_install_property (oclass, PROP_RUNNER, + g_param_spec_object ("validate-runner", "VALIDATE Runner", + "The Validate runner to " "report errors to", + GST_TYPE_VALIDATE_RUNNER, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); +} + +static void +gst_validate_override_init (GstValidateOverride * self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + GST_TYPE_VALIDATE_OVERRIDE, GstValidateOverridePriv); + + self->priv->level_override = g_hash_table_new (g_direct_hash, g_direct_equal); +} + +GstValidateOverride * +gst_validate_override_new (void) +{ + return g_object_new (GST_TYPE_VALIDATE_OVERRIDE, NULL); +} + +void +gst_validate_override_change_severity (GstValidateOverride * override, + GstValidateIssueId issue_id, GstValidateReportLevel new_level) +{ + g_hash_table_insert (override->priv->level_override, (gpointer) issue_id, + (gpointer) new_level); +} + +/* + * Also receives @default_level to preserve a custom level that might have + * been set by a previous GstValidateOverride and should not go back to the + * GstValidateIssue default + */ +GstValidateReportLevel +gst_validate_override_get_severity (GstValidateOverride * override, + GstValidateIssueId issue_id, GstValidateReportLevel default_level) +{ + GstValidateReportLevel *level = NULL; + + if (g_hash_table_lookup_extended (override->priv->level_override, + (gpointer) issue_id, NULL, (gpointer) & level)) { + + return GPOINTER_TO_INT (level); + } + return default_level; +} + +void +gst_validate_override_set_event_handler (GstValidateOverride * override, + GstValidateOverrideEventHandler handler) +{ + override->event_handler = handler; +} + +void +gst_validate_override_set_buffer_handler (GstValidateOverride * override, + GstValidateOverrideBufferHandler handler) +{ + override->buffer_handler = handler; +} + +void +gst_validate_override_set_query_handler (GstValidateOverride * override, + GstValidateOverrideQueryHandler handler) +{ + override->query_handler = handler; +} + +void +gst_validate_override_set_buffer_probe_handler (GstValidateOverride * override, + GstValidateOverrideBufferHandler handler) +{ + override->buffer_probe_handler = handler; +} + +void +gst_validate_override_set_getcaps_handler (GstValidateOverride * override, + GstValidateOverrideGetCapsHandler handler) +{ + override->getcaps_handler = handler; +} + +void +gst_validate_override_set_setcaps_handler (GstValidateOverride * override, + GstValidateOverrideSetCapsHandler handler) +{ + override->setcaps_handler = handler; +} + +void +gst_validate_override_event_handler (GstValidateOverride * override, + GstValidateMonitor * monitor, GstEvent * event) +{ + if (override->event_handler) + override->event_handler (override, monitor, event); +} + +void +gst_validate_override_buffer_handler (GstValidateOverride * override, + GstValidateMonitor * monitor, GstBuffer * buffer) +{ + if (override->buffer_handler) + override->buffer_handler (override, monitor, buffer); +} + +void +gst_validate_override_query_handler (GstValidateOverride * override, + GstValidateMonitor * monitor, GstQuery * query) +{ + if (override->query_handler) + override->query_handler (override, monitor, query); +} + +void +gst_validate_override_buffer_probe_handler (GstValidateOverride * override, + GstValidateMonitor * monitor, GstBuffer * buffer) +{ + if (override->buffer_probe_handler) + override->buffer_probe_handler (override, monitor, buffer); +} + +void +gst_validate_override_getcaps_handler (GstValidateOverride * override, + GstValidateMonitor * monitor, GstCaps * caps) +{ + if (override->getcaps_handler) + override->getcaps_handler (override, monitor, caps); +} + +void +gst_validate_override_setcaps_handler (GstValidateOverride * override, + GstValidateMonitor * monitor, GstCaps * caps) +{ + if (override->setcaps_handler) + override->setcaps_handler (override, monitor, caps); +} + +gboolean +gst_validate_override_can_attach (GstValidateOverride * override, + GstValidateMonitor * monitor) +{ + GstValidateOverrideClass *klass = GST_VALIDATE_OVERRIDE_GET_CLASS (override); + + if (klass->can_attach) + return klass->can_attach (override, monitor); + + return TRUE; +} diff --git a/validate/gst/validate/gst-validate-override.h b/validate/gst/validate/gst-validate-override.h new file mode 100644 index 0000000..d2dc32e --- /dev/null +++ b/validate/gst/validate/gst-validate-override.h @@ -0,0 +1,107 @@ +/* GStreamer + * Copyright (C) 2013 Thiago Santos + * + * gst-validate-override.h - Validate Override that allows customizing Validate behavior + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_VALIDATE_OVERRIDE_H__ +#define __GST_VALIDATE_OVERRIDE_H__ + +#include +#include + +typedef struct _GstValidateOverride GstValidateOverride; +typedef struct _GstValidateOverrideClass GstValidateOverrideClass; +typedef struct _GstValidateOverridePriv GstValidateOverridePriv; + + +#include +#include + +G_BEGIN_DECLS + +typedef void (*GstValidateOverrideBufferHandler)(GstValidateOverride * override, + GstValidateMonitor * pad_monitor, GstBuffer * buffer); +typedef void (*GstValidateOverrideEventHandler)(GstValidateOverride * override, + GstValidateMonitor * pad_monitor, GstEvent * event); +typedef void (*GstValidateOverrideQueryHandler)(GstValidateOverride * override, + GstValidateMonitor * pad_monitor, GstQuery * query); +typedef void (*GstValidateOverrideGetCapsHandler)(GstValidateOverride * override, + GstValidateMonitor * pad_monitor, GstCaps * caps); +typedef void (*GstValidateOverrideSetCapsHandler)(GstValidateOverride * override, + GstValidateMonitor * pad_monitor, GstCaps * caps); + +struct _GstValidateOverrideClass +{ + /**/ + GObjectClass parent_class; + + gboolean (*can_attach)(GstValidateOverride * override, + GstValidateMonitor * monitor); + +}; + +struct _GstValidateOverride +{ + GObject parent; + + GstValidateOverrideBufferHandler buffer_handler; + GstValidateOverrideEventHandler event_handler; + GstValidateOverrideQueryHandler query_handler; + GstValidateOverrideBufferHandler buffer_probe_handler; + GstValidateOverrideGetCapsHandler getcaps_handler; + GstValidateOverrideSetCapsHandler setcaps_handler; + + /**/ + GstValidateOverridePriv *priv; +}; + +GType gst_validate_override_get_type (void) G_GNUC_CONST; + +/* TYPE MACROS */ +#define GST_TYPE_VALIDATE_OVERRIDE (gst_validate_override_get_type ()) +#define GST_VALIDATE_OVERRIDE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_VALIDATE_OVERRIDE, GstValidateOverride)) +#define GST_VALIDATE_OVERRIDE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_VALIDATE_OVERRIDE, GstValidateOverrideClass)) +#define GST_IS_VALIDATE_OVERRIDE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_VALIDATE_OVERRIDE)) +#define GST_IS_VALIDATE_OVERRIDE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_VALIDATE_OVERRIDE)) +#define GST_VALIDATE_OVERRIDE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_VALIDATE_OVERRIDE, GstValidateOverrideClass)) + +GstValidateOverride * gst_validate_override_new (void); + +void gst_validate_override_free (GstValidateOverride * override); +void gst_validate_override_change_severity (GstValidateOverride * override, GstValidateIssueId issue_id, GstValidateReportLevel new_level); +GstValidateReportLevel gst_validate_override_get_severity (GstValidateOverride * override, GstValidateIssueId issue_id, GstValidateReportLevel default_level); + +void gst_validate_override_event_handler (GstValidateOverride * override, GstValidateMonitor * monitor, GstEvent * event); +void gst_validate_override_buffer_handler (GstValidateOverride * override, GstValidateMonitor * monitor, GstBuffer * buffer); +void gst_validate_override_query_handler (GstValidateOverride * override, GstValidateMonitor * monitor, GstQuery * query); +void gst_validate_override_buffer_probe_handler (GstValidateOverride * override, GstValidateMonitor * monitor, GstBuffer * buffer); +void gst_validate_override_getcaps_handler (GstValidateOverride * override, GstValidateMonitor * monitor, GstCaps * caps); +void gst_validate_override_setcaps_handler (GstValidateOverride * override, GstValidateMonitor * monitor, GstCaps * caps); + +void gst_validate_override_set_event_handler (GstValidateOverride * override, GstValidateOverrideEventHandler handler); +void gst_validate_override_set_buffer_handler (GstValidateOverride * override, GstValidateOverrideBufferHandler handler); +void gst_validate_override_set_query_handler (GstValidateOverride * override, GstValidateOverrideQueryHandler handler); +void gst_validate_override_set_buffer_probe_handler (GstValidateOverride * override, GstValidateOverrideBufferHandler handler); +void gst_validate_override_set_getcaps_handler (GstValidateOverride * override, GstValidateOverrideGetCapsHandler handler); +void gst_validate_override_set_setcaps_handler (GstValidateOverride * override, GstValidateOverrideSetCapsHandler handler); +gboolean gst_validate_override_can_attach (GstValidateOverride * override, GstValidateMonitor *monitor); + +G_END_DECLS + +#endif /* #ifndef __GST_VALIDATE_OVERRIDE_H__*/ diff --git a/validate/gst/validate/gst-validate-pad-monitor.c b/validate/gst/validate/gst-validate-pad-monitor.c new file mode 100644 index 0000000..2df9845 --- /dev/null +++ b/validate/gst/validate/gst-validate-pad-monitor.c @@ -0,0 +1,2564 @@ +/* GStreamer + * + * Copyright (C) 2013 Collabora Ltd. + * Author: Thiago Sousa Santos + * + * gst-validate-pad-monitor.c - Validate PadMonitor class + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "gst-validate-internal.h" +#include "gst-validate-pad-monitor.h" +#include "gst-validate-element-monitor.h" +#include "gst-validate-pipeline-monitor.h" +#include "gst-validate-reporter.h" +#include +#include + +/** + * SECTION:gst-validate-pad-monitor + * @short_description: Class that wraps a #GstPad for Validate checks + * + * TODO + */ + +static GstValidateInterceptionReturn +gst_validate_pad_monitor_intercept_report (GstValidateReporter * reporter, + GstValidateReport * report); + +#define _do_init \ + G_IMPLEMENT_INTERFACE (GST_TYPE_VALIDATE_REPORTER, _reporter_iface_init) + +static void +_reporter_iface_init (GstValidateReporterInterface * iface) +{ + iface->intercept_report = gst_validate_pad_monitor_intercept_report; +} + +#define gst_validate_pad_monitor_parent_class parent_class +G_DEFINE_TYPE_WITH_CODE (GstValidatePadMonitor, gst_validate_pad_monitor, + GST_TYPE_VALIDATE_MONITOR, _do_init); + +#define PENDING_FIELDS "pending-fields" +#define AUDIO_TIMESTAMP_TOLERANCE (GST_MSECOND * 100) + +#define PAD_PARENT_IS_DEMUXER(m) \ + (GST_VALIDATE_MONITOR_GET_PARENT(m) ? \ + GST_VALIDATE_ELEMENT_MONITOR_ELEMENT_IS_DEMUXER ( \ + GST_VALIDATE_MONITOR_GET_PARENT(m)) : \ + FALSE) + +#define PAD_PARENT_IS_DECODER(m) \ + (GST_VALIDATE_MONITOR_GET_PARENT(m) ? \ + GST_VALIDATE_ELEMENT_MONITOR_ELEMENT_IS_DECODER ( \ + GST_VALIDATE_MONITOR_GET_PARENT(m)) : \ + FALSE) + +#define PAD_PARENT_IS_ENCODER(m) \ + (GST_VALIDATE_MONITOR_GET_PARENT(m) ? \ + GST_VALIDATE_ELEMENT_MONITOR_ELEMENT_IS_ENCODER ( \ + GST_VALIDATE_MONITOR_GET_PARENT(m)) : \ + FALSE) + + +/* + * Locking the parent should always be done before locking the + * pad-monitor to prevent deadlocks in case another monitor from + * another pad on the same element starts an operation that also + * requires locking itself and some other monitors from internally + * linked pads. + * + * An example: + * An element has a sink and a src pad. Some test starts running at sinkpad + * and it locks the parent, and then it locks itself. In case it needs to get + * some information from the srcpad, it is able to lock the srcpad and get it + * because the srcpad should never lock itself before locking the parent (which + * it won't be able as sinkpad already locked it). + * + * As a side one, it is possible that srcpad locks itself without locking the + * parent in case it wants to do a check that won't need to use other internally + * linked pads (sinkpad). But in this case it might lock and unlock freely without + * causing deadlocks. + */ +#define GST_VALIDATE_PAD_MONITOR_PARENT_LOCK(m) \ +G_STMT_START { \ + if (G_LIKELY (GST_VALIDATE_MONITOR_GET_PARENT (m))) { \ + GST_VALIDATE_MONITOR_LOCK (GST_VALIDATE_MONITOR_GET_PARENT (m)); \ + } else { \ + GST_WARNING_OBJECT (m, "No parent found, can't lock"); \ + } \ +} G_STMT_END + +#define GST_VALIDATE_PAD_MONITOR_PARENT_UNLOCK(m) \ +G_STMT_START { \ + if (G_LIKELY (GST_VALIDATE_MONITOR_GET_PARENT (m))) { \ + GST_VALIDATE_MONITOR_UNLOCK (GST_VALIDATE_MONITOR_GET_PARENT (m)); \ + } else { \ + GST_WARNING_OBJECT (m, "No parent found, can't unlock"); \ + } \ +} G_STMT_END + +typedef struct +{ + GstClockTime timestamp; + GstEvent *event; +} SerializedEventData; + +static GstPad * +_get_actual_pad (GstPad * pad) +{ + GstPad *tmp_pad; + + gst_object_ref (pad); + + /* We don't monitor ghost pads */ + while (GST_IS_GHOST_PAD (pad)) { + tmp_pad = pad; + pad = gst_ghost_pad_get_target ((GstGhostPad *) pad); + gst_object_unref (tmp_pad); + } + + while (GST_IS_PROXY_PAD (pad)) { + tmp_pad = pad; + pad = gst_pad_get_peer (pad); + gst_object_unref (tmp_pad); + } + + return pad; +} + +static gboolean +_find_master_report_on_pad (GstPad * pad, GstValidateReport * report) +{ + GstValidatePadMonitor *pad_monitor; + GstValidateReport *prev_report; + gboolean result = FALSE; + GstPad *tmppad = pad; + + pad = _get_actual_pad (pad); + if (pad == NULL) { + GST_ERROR_OBJECT (tmppad, "Does not have a target yet"); + + return FALSE; + } + + pad_monitor = g_object_get_data ((GObject *) pad, "validate-monitor"); + + /* For some reason this pad isn't monitored */ + if (pad_monitor == NULL) + goto done; + + prev_report = gst_validate_reporter_get_report ((GstValidateReporter *) + pad_monitor, report->issue->issue_id); + + if (prev_report) { + if (prev_report->master_report) + result = gst_validate_report_set_master_report (report, + prev_report->master_report); + else + result = gst_validate_report_set_master_report (report, prev_report); + } + +done: + gst_object_unref (pad); + + return result; +} + +static gboolean +_find_master_report_for_sink_pad (GstValidatePadMonitor * pad_monitor, + GstValidateReport * report) +{ + GstPad *peerpad; + gboolean result = FALSE; + + peerpad = gst_pad_get_peer (pad_monitor->pad); + + /* If the peer src pad already has a similar report no need to look + * any further */ + if (peerpad && _find_master_report_on_pad (peerpad, report)) + result = TRUE; + + if (peerpad) + gst_object_unref (peerpad); + + return result; +} + +static gboolean +_find_master_report_for_src_pad (GstValidatePadMonitor * pad_monitor, + GstValidateReport * report) +{ + GstIterator *iter; + gboolean done; + GstPad *pad; + gboolean result = FALSE; + + iter = + gst_pad_iterate_internal_links (GST_VALIDATE_PAD_MONITOR_GET_PAD + (pad_monitor)); + done = FALSE; + while (!done) { + GValue value = { 0, }; + switch (gst_iterator_next (iter, &value)) { + case GST_ITERATOR_OK: + pad = g_value_get_object (&value); + + if (_find_master_report_on_pad (pad, report)) { + result = TRUE; + done = TRUE; + } + + g_value_reset (&value); + break; + case GST_ITERATOR_RESYNC: + gst_iterator_resync (iter); + break; + case GST_ITERATOR_ERROR: + GST_WARNING_OBJECT (pad_monitor->pad, + "Internal links pad iteration error"); + done = TRUE; + break; + case GST_ITERATOR_DONE: + done = TRUE; + break; + } + } + gst_iterator_free (iter); + + return result; +} + +static GstValidateInterceptionReturn +_concatenate_issues (GstValidatePadMonitor * pad_monitor, + GstValidateReport * report) +{ + if (GST_PAD_IS_SINK (pad_monitor->pad) + && _find_master_report_for_sink_pad (pad_monitor, report)) + return GST_VALIDATE_REPORTER_KEEP; + else if (GST_PAD_IS_SRC (pad_monitor->pad) + && _find_master_report_for_src_pad (pad_monitor, report)) + return GST_VALIDATE_REPORTER_KEEP; + + return GST_VALIDATE_REPORTER_REPORT; +} + +static GstValidateInterceptionReturn +gst_validate_pad_monitor_intercept_report (GstValidateReporter * + reporter, GstValidateReport * report) +{ + GstValidateReporterInterface *iface_class, *old_iface_class; + GstValidatePadMonitor *pad_monitor = GST_VALIDATE_PAD_MONITOR (reporter); + GstValidateReportingDetails monitor_reporting_level; + GstValidateInterceptionReturn ret; + + monitor_reporting_level = + gst_validate_reporter_get_reporting_level (reporter); + + iface_class = + G_TYPE_INSTANCE_GET_INTERFACE (reporter, GST_TYPE_VALIDATE_REPORTER, + GstValidateReporterInterface); + old_iface_class = g_type_interface_peek_parent (iface_class); + + old_iface_class->intercept_report (reporter, report); + + switch (monitor_reporting_level) { + case GST_VALIDATE_SHOW_NONE: + ret = GST_VALIDATE_REPORTER_DROP; + break; + case GST_VALIDATE_SHOW_UNKNOWN: + ret = _concatenate_issues (pad_monitor, report); + break; + default: + ret = GST_VALIDATE_REPORTER_REPORT; + break; + } + + gst_validate_report_set_reporting_level (report, monitor_reporting_level); + return ret; +} + +static void +debug_pending_event (GstPad * pad, GPtrArray * array) +{ + guint i, len; + + len = array->len; + for (i = 0; i < len; i++) { + SerializedEventData *data = g_ptr_array_index (array, i); + GST_DEBUG_OBJECT (pad, "event #%d %" GST_TIME_FORMAT " %s %p", + i, GST_TIME_ARGS (data->timestamp), + GST_EVENT_TYPE_NAME (data->event), data->event); + } +} + +static void +_serialized_event_data_free (SerializedEventData * serialized_event) +{ + gst_event_unref (serialized_event->event); + g_slice_free (SerializedEventData, serialized_event); +} + +static gboolean gst_validate_pad_monitor_do_setup (GstValidateMonitor * + monitor); +static GstElement *gst_validate_pad_monitor_get_element (GstValidateMonitor * + monitor); +static void +gst_validate_pad_monitor_setcaps_pre (GstValidatePadMonitor * pad_monitor, + GstCaps * caps); +static void gst_validate_pad_monitor_setcaps_post (GstValidatePadMonitor * + pad_monitor, GstCaps * caps, gboolean ret); + +#define PAD_IS_IN_PUSH_MODE(p) ((p)->mode == GST_PAD_MODE_PUSH) + +static gboolean +_structure_is_raw_video (GstStructure * structure) +{ + return gst_structure_has_name (structure, "video/x-raw"); +} + +static gboolean +_structure_is_raw_audio (GstStructure * structure) +{ + return gst_structure_has_name (structure, "audio/x-raw"); +} + +static gchar * +_get_event_string (GstEvent * event) +{ + const GstStructure *st; + + if ((st = gst_event_get_structure (event))) + return gst_structure_to_string (st); + else + return g_strdup_printf ("%s", GST_EVENT_TYPE_NAME (event)); +} + +static void +_check_field_type (GstValidatePadMonitor * monitor, + GstStructure * structure, gboolean mandatory, const gchar * field, ...) +{ + va_list var_args; + GType type; + gchar *joined_types = NULL; + const gchar *rejected_types[5]; + gint rejected_types_index = 0; + gchar *struct_str; + + if (!gst_structure_has_field (structure, field)) { + if (mandatory) { + gchar *str = gst_structure_to_string (structure); + + GST_VALIDATE_REPORT (monitor, CAPS_IS_MISSING_FIELD, + "Field '%s' is missing from structure: %s", field, str); + g_free (str); + } else { + GST_DEBUG_OBJECT (monitor, "Field %s is missing but is not mandatory", + field); + } + return; + } + + memset (rejected_types, 0, sizeof (rejected_types)); + va_start (var_args, field); + while ((type = va_arg (var_args, GType)) != 0) { + if (gst_structure_has_field_typed (structure, field, type)) { + va_end (var_args); + return; + } + rejected_types[rejected_types_index++] = g_type_name (type); + } + va_end (var_args); + + joined_types = g_strjoinv (" / ", (gchar **) rejected_types); + struct_str = gst_structure_to_string (structure); + GST_VALIDATE_REPORT (monitor, CAPS_FIELD_HAS_BAD_TYPE, + "Field '%s' has wrong type %s in structure '%s'. Expected: %s", field, + g_type_name (gst_structure_get_field_type (structure, field)), struct_str, + joined_types); + g_free (joined_types); + g_free (struct_str); +} + +static void +gst_validate_pad_monitor_check_raw_video_caps_complete (GstValidatePadMonitor * + monitor, GstStructure * structure) +{ + _check_field_type (monitor, structure, TRUE, "width", G_TYPE_INT, + GST_TYPE_INT_RANGE, 0); + _check_field_type (monitor, structure, TRUE, "height", G_TYPE_INT, + GST_TYPE_INT_RANGE, 0); + _check_field_type (monitor, structure, TRUE, "framerate", GST_TYPE_FRACTION, + GST_TYPE_FRACTION_RANGE, 0); + _check_field_type (monitor, structure, FALSE, "pixel-aspect-ratio", + GST_TYPE_FRACTION, GST_TYPE_FRACTION_RANGE, 0); + _check_field_type (monitor, structure, TRUE, "format", G_TYPE_STRING, + GST_TYPE_LIST); +} + +static void +gst_validate_pad_monitor_check_raw_audio_caps_complete (GstValidatePadMonitor * + monitor, GstStructure * structure) +{ + gint channels; + _check_field_type (monitor, structure, TRUE, "format", G_TYPE_STRING, + GST_TYPE_LIST, 0); + _check_field_type (monitor, structure, TRUE, "layout", G_TYPE_STRING, + GST_TYPE_LIST, 0); + _check_field_type (monitor, structure, TRUE, "rate", G_TYPE_INT, + GST_TYPE_LIST, GST_TYPE_INT_RANGE, 0); + _check_field_type (monitor, structure, TRUE, "channels", G_TYPE_INT, + GST_TYPE_LIST, GST_TYPE_INT_RANGE, 0); + if (gst_structure_get_int (structure, "channels", &channels)) { + if (channels > 2) + _check_field_type (monitor, structure, TRUE, "channel-mask", + GST_TYPE_BITMASK, GST_TYPE_LIST, 0); + } +} + +static void +gst_validate_pad_monitor_check_caps_complete (GstValidatePadMonitor * monitor, + GstCaps * caps) +{ + GstStructure *structure; + gint i; + + GST_DEBUG_OBJECT (monitor->pad, "Checking caps %" GST_PTR_FORMAT, caps); + + for (i = 0; i < gst_caps_get_size (caps); i++) { + structure = gst_caps_get_structure (caps, i); + + if (_structure_is_raw_video (structure)) { + gst_validate_pad_monitor_check_raw_video_caps_complete (monitor, + structure); + + } else if (_structure_is_raw_audio (structure)) { + gst_validate_pad_monitor_check_raw_audio_caps_complete (monitor, + structure); + } + } +} + +static GstCaps * +gst_validate_pad_monitor_get_othercaps (GstValidatePadMonitor * monitor, + GstCaps * filter) +{ + GstCaps *caps = gst_caps_new_empty (); + GstIterator *iter; + gboolean done; + GstPad *otherpad; + GstCaps *peercaps; + + iter = + gst_pad_iterate_internal_links (GST_VALIDATE_PAD_MONITOR_GET_PAD + (monitor)); + done = FALSE; + while (!done) { + GValue value = { 0, }; + switch (gst_iterator_next (iter, &value)) { + case GST_ITERATOR_OK: + otherpad = g_value_get_object (&value); + + /* TODO What would be the correct caps operation to merge the caps in + * case one sink is internally linked to multiple srcs? */ + peercaps = gst_pad_peer_query_caps (otherpad, filter); + if (peercaps) + caps = gst_caps_merge (caps, peercaps); + + g_value_reset (&value); + break; + case GST_ITERATOR_RESYNC: + gst_iterator_resync (iter); + gst_caps_unref (caps); + caps = gst_caps_new_empty (); + break; + case GST_ITERATOR_ERROR: + GST_WARNING_OBJECT (monitor->pad, "Internal links pad iteration error"); + done = TRUE; + break; + case GST_ITERATOR_DONE: + done = TRUE; + break; + } + } + gst_iterator_free (iter); + + GST_DEBUG_OBJECT (monitor->pad, "Otherpad caps: %" GST_PTR_FORMAT, caps); + + return caps; +} + +static gboolean +_structure_is_video (GstStructure * structure) +{ + const gchar *name = gst_structure_get_name (structure); + + return g_strstr_len (name, 6, "video/") + && strcmp (name, "video/quicktime") != 0; +} + +static gboolean +_structure_is_audio (GstStructure * structure) +{ + const gchar *name = gst_structure_get_name (structure); + + return g_strstr_len (name, 6, "audio/") != NULL; +} + +static gboolean +gst_validate_pad_monitor_pad_should_proxy_othercaps (GstValidatePadMonitor * + monitor) +{ + GstValidateMonitor *parent = GST_VALIDATE_MONITOR_GET_PARENT (monitor); + + if (!parent) + return FALSE; + + /* We only know how to handle othercaps checks for codecs so far */ + return (GST_VALIDATE_ELEMENT_MONITOR_ELEMENT_IS_DECODER (parent) || + GST_VALIDATE_ELEMENT_MONITOR_ELEMENT_IS_ENCODER (parent)) && + !GST_VALIDATE_ELEMENT_MONITOR_ELEMENT_IS_CONVERTER (parent); +} + + +/* Check if the field @f from @s2 (if present) is represented in @s1 + * Represented here means either equal or @s1's value is in a list/range + * from @s2 + */ +static gboolean +_structures_field_is_contained (GstStructure * s1, GstStructure * s2, + gboolean mandatory, const gchar * f) +{ + const GValue *v1; + const GValue *v2; + + v2 = gst_structure_get_value (s2, f); + if (!v2) + return TRUE; /* nothing to compare to */ + + v1 = gst_structure_get_value (s1, f); + if (!v1) + return !mandatory; + + if (!gst_value_is_fixed (v1)) + return TRUE; + + if (gst_value_compare (v1, v2) == GST_VALUE_EQUAL) + return TRUE; + + if (GST_VALUE_HOLDS_LIST (v2)) { + gint i; + for (i = 0; i < gst_value_list_get_size (v2); i++) { + const GValue *v2_subvalue = gst_value_list_get_value (v2, i); + if (gst_value_compare (v1, v2_subvalue) == GST_VALUE_EQUAL) + return TRUE; + } + } + + if (GST_VALUE_HOLDS_ARRAY (v2)) { + gint i; + for (i = 0; i < gst_value_array_get_size (v2); i++) { + const GValue *v2_subvalue = gst_value_array_get_value (v2, i); + if (gst_value_compare (v1, v2_subvalue) == GST_VALUE_EQUAL) + return TRUE; + } + } + + if (GST_VALUE_HOLDS_INT_RANGE (v2)) { + gint min, max; + + min = gst_value_get_int_range_min (v2); + max = gst_value_get_int_range_max (v2); + + if (G_VALUE_HOLDS_INT (v1)) { + gint v = g_value_get_int (v1); + + return v >= min && v <= max; + } else { + /* TODO compare int ranges with int ranges + * or with lists if useful */ + } + } + + if (GST_VALUE_HOLDS_FRACTION_RANGE (v2)) { + const GValue *min, *max; + + min = gst_value_get_fraction_range_min (v2); + max = gst_value_get_fraction_range_max (v2); + + if (GST_VALUE_HOLDS_FRACTION (v1)) { + gint v_min = gst_value_compare (v1, min); + gint v_max = gst_value_compare (v1, max); + + return (v_min == GST_VALUE_EQUAL || v_min == GST_VALUE_GREATER_THAN) && + (v_max == GST_VALUE_EQUAL || v_max == GST_VALUE_LESS_THAN); + } else { + /* TODO compare fraction ranges with fraction ranges + * or with lists if useful */ + } + } + + return FALSE; +} + +static void +_check_and_copy_structure_field (GstStructure * from, GstStructure * to, + const gchar * name) +{ + if (gst_structure_has_field (from, name)) { + gst_structure_set_value (to, name, gst_structure_get_value (from, name)); + } +} + +static GstCaps * +gst_validate_pad_monitor_copy_caps_fields_into_caps (GstValidatePadMonitor * + monitor, GstCaps * from_caps, GstCaps * into_caps) +{ + gint i, j, into_size, from_size; + GstStructure *structure; + GstCaps *res = gst_caps_new_empty (); + + into_size = gst_caps_get_size (into_caps); + from_size = gst_caps_get_size (from_caps); + + for (i = 0; i < into_size; i++) { + GstStructure *s = gst_caps_get_structure (into_caps, i); + + for (j = 0; j < from_size; j++) { + GstStructure *new_structure = gst_structure_copy (s); + + structure = gst_caps_get_structure (from_caps, j); + if (_structure_is_video (structure)) { + _check_and_copy_structure_field (structure, new_structure, "width"); + _check_and_copy_structure_field (structure, new_structure, "height"); + _check_and_copy_structure_field (structure, new_structure, "framerate"); + _check_and_copy_structure_field (structure, new_structure, + "pixel-aspect-ratio"); + } else if (_structure_is_audio (s)) { + _check_and_copy_structure_field (structure, new_structure, "rate"); + _check_and_copy_structure_field (structure, new_structure, "channels"); + } + + gst_caps_append_structure (res, new_structure); + } + } + return res; +} + +static GstCaps * +gst_validate_pad_monitor_transform_caps (GstValidatePadMonitor * monitor, + GstCaps * caps) +{ + GstCaps *othercaps; + GstCaps *new_caps; + GstIterator *iter; + gboolean done; + GstPad *otherpad; + GstCaps *template_caps; + + GST_DEBUG_OBJECT (monitor->pad, "Transform caps %" GST_PTR_FORMAT, caps); + + if (caps == NULL) + return NULL; + + othercaps = gst_caps_new_empty (); + + iter = + gst_pad_iterate_internal_links (GST_VALIDATE_PAD_MONITOR_GET_PAD + (monitor)); + done = FALSE; + while (!done) { + GValue value = { 0, }; + switch (gst_iterator_next (iter, &value)) { + case GST_ITERATOR_OK: + otherpad = g_value_get_object (&value); + template_caps = gst_pad_get_pad_template_caps (otherpad); + + new_caps = + gst_validate_pad_monitor_copy_caps_fields_into_caps (monitor, caps, + template_caps); + if (!gst_caps_is_empty (new_caps)) + gst_caps_append (othercaps, new_caps); + else + gst_caps_unref (new_caps); + + gst_caps_unref (template_caps); + g_value_reset (&value); + break; + case GST_ITERATOR_RESYNC: + gst_iterator_resync (iter); + gst_caps_unref (othercaps); + othercaps = gst_caps_new_empty (); + break; + case GST_ITERATOR_ERROR: + GST_WARNING_OBJECT (monitor->pad, "Internal links pad iteration error"); + done = TRUE; + break; + case GST_ITERATOR_DONE: + done = TRUE; + break; + } + } + gst_iterator_free (iter); + + GST_DEBUG_OBJECT (monitor->pad, "Transformed caps: %" GST_PTR_FORMAT, + othercaps); + + return othercaps; +} + +static void +gst_validate_pad_monitor_check_caps_fields_proxied (GstValidatePadMonitor * + monitor, GstCaps * caps, GstCaps * filter) +{ + GstStructure *structure; + GstStructure *otherstructure; + GstCaps *othercaps; + GstCaps *otherfilter; + gint i, j; + + if (!gst_validate_pad_monitor_pad_should_proxy_othercaps (monitor)) + return; + + otherfilter = gst_validate_pad_monitor_transform_caps (monitor, filter); + othercaps = gst_validate_pad_monitor_get_othercaps (monitor, otherfilter); + if (otherfilter) + gst_caps_unref (otherfilter); + + for (i = 0; i < gst_caps_get_size (othercaps); i++) { + gboolean found = FALSE; + gboolean type_match = FALSE; + + otherstructure = gst_caps_get_structure (othercaps, i); + + /* look for a proxied version of 'otherstructure' */ + if (_structure_is_video (otherstructure)) { + for (j = 0; j < gst_caps_get_size (caps); j++) { + structure = gst_caps_get_structure (caps, j); + if (_structure_is_video (structure)) { + type_match = TRUE; + if (_structures_field_is_contained (structure, otherstructure, TRUE, + "width") + && _structures_field_is_contained (structure, otherstructure, + TRUE, "height") + && _structures_field_is_contained (structure, otherstructure, + TRUE, "framerate") + && _structures_field_is_contained (structure, otherstructure, + FALSE, "pixel-aspect-ratio")) { + found = TRUE; + break; + } + } + } + } else if (_structure_is_audio (otherstructure)) { + for (j = 0; j < gst_caps_get_size (caps); j++) { + structure = gst_caps_get_structure (caps, j); + if (_structure_is_audio (structure)) { + type_match = TRUE; + if (_structures_field_is_contained (structure, otherstructure, TRUE, + "rate") + && _structures_field_is_contained (structure, otherstructure, + TRUE, "channels")) { + found = TRUE; + break; + } + } + } + } + + if (type_match && !found) { + gchar *otherstruct_str = gst_structure_to_string (otherstructure), + *caps_str = gst_caps_to_string (caps); + + GST_VALIDATE_REPORT (monitor, GET_CAPS_NOT_PROXYING_FIELDS, + "Peer pad structure '%s' has no similar version " + "on pad's caps '%s'", otherstruct_str, caps_str); + + g_free (otherstruct_str); + g_free (caps_str); + } + } + + gst_caps_unref (othercaps); +} + +static void +gst_validate_pad_monitor_check_late_serialized_events (GstValidatePadMonitor * + monitor, GstClockTime ts) +{ + gint i; + + if (!GST_CLOCK_TIME_IS_VALID (ts)) + return; + + GST_DEBUG_OBJECT (monitor->pad, "Timestamp to check %" GST_TIME_FORMAT, + GST_TIME_ARGS (ts)); + + for (i = 0; i < monitor->serialized_events->len; i++) { + SerializedEventData *data = + g_ptr_array_index (monitor->serialized_events, i); + + GST_DEBUG_OBJECT (monitor->pad, "Event #%d (%s) ts: %" GST_TIME_FORMAT, + i, GST_EVENT_TYPE_NAME (data->event), GST_TIME_ARGS (data->timestamp)); + + if (GST_CLOCK_TIME_IS_VALID (data->timestamp) && data->timestamp < ts) { + gchar *event_str = _get_event_string (data->event); + + GST_VALIDATE_REPORT (monitor, SERIALIZED_EVENT_WASNT_PUSHED_IN_TIME, + "Serialized event %s wasn't pushed before expected " "timestamp %" + GST_TIME_FORMAT " on pad %s:%s", event_str, + GST_TIME_ARGS (data->timestamp), + GST_DEBUG_PAD_NAME (GST_VALIDATE_PAD_MONITOR_GET_PAD (monitor))); + + g_free (event_str); + } else { + /* events should be ordered by ts */ + break; + } + } + + if (i) { + debug_pending_event (monitor->pad, monitor->serialized_events); + g_ptr_array_remove_range (monitor->serialized_events, 0, i); + } +} + +static void +gst_validate_pad_monitor_dispose (GObject * object) +{ + GstValidatePadMonitor *monitor = GST_VALIDATE_PAD_MONITOR_CAST (object); + GstPad *pad = GST_VALIDATE_PAD_MONITOR_GET_PAD (monitor); + + if (pad) { + if (monitor->pad_probe_id) + gst_pad_remove_probe (pad, monitor->pad_probe_id); + } + + if (monitor->expected_segment) + gst_event_unref (monitor->expected_segment); + + gst_structure_free (monitor->pending_setcaps_fields); + g_ptr_array_unref (monitor->serialized_events); + g_list_free_full (monitor->expired_events, (GDestroyNotify) gst_event_unref); + g_list_free_full (monitor->all_bufs, (GDestroyNotify) gst_buffer_unref); + gst_caps_replace (&monitor->last_caps, NULL); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gst_validate_pad_monitor_class_init (GstValidatePadMonitorClass * klass) +{ + GObjectClass *gobject_class; + GstValidateMonitorClass *monitor_klass; + + gobject_class = G_OBJECT_CLASS (klass); + monitor_klass = GST_VALIDATE_MONITOR_CLASS (klass); + + gobject_class->dispose = gst_validate_pad_monitor_dispose; + + monitor_klass->setup = gst_validate_pad_monitor_do_setup; + monitor_klass->get_element = gst_validate_pad_monitor_get_element; +} + +static void +gst_validate_pad_monitor_init (GstValidatePadMonitor * pad_monitor) +{ + pad_monitor->pending_setcaps_fields = + gst_structure_new_empty (PENDING_FIELDS); + pad_monitor->serialized_events = + g_ptr_array_new_with_free_func ((GDestroyNotify) + _serialized_event_data_free); + pad_monitor->expired_events = NULL; + gst_segment_init (&pad_monitor->segment, GST_FORMAT_BYTES); + pad_monitor->first_buffer = TRUE; + + pad_monitor->timestamp_range_start = GST_CLOCK_TIME_NONE; + pad_monitor->timestamp_range_end = GST_CLOCK_TIME_NONE; +} + +/** + * gst_validate_pad_monitor_new: + * @pad: (transfer-none): a #GstPad to run Validate on + */ +GstValidatePadMonitor * +gst_validate_pad_monitor_new (GstPad * pad, GstValidateRunner * runner, + GstValidateElementMonitor * parent) +{ + GstValidatePadMonitor *monitor = g_object_new (GST_TYPE_VALIDATE_PAD_MONITOR, + "object", pad, "validate-runner", runner, "validate-parent", + parent, NULL); + + if (GST_VALIDATE_PAD_MONITOR_GET_PAD (monitor) == NULL) { + g_object_unref (monitor); + return NULL; + } + return monitor; +} + +static GstElement * +gst_validate_pad_monitor_get_element (GstValidateMonitor * monitor) +{ + GstPad *pad = GST_VALIDATE_PAD_MONITOR_GET_PAD (monitor); + + return GST_PAD_PARENT (pad); +} + +static void +gst_validate_pad_monitor_event_overrides (GstValidatePadMonitor * pad_monitor, + GstEvent * event) +{ + GList *iter; + + GST_VALIDATE_MONITOR_OVERRIDES_LOCK (pad_monitor); + for (iter = GST_VALIDATE_MONITOR_OVERRIDES (pad_monitor).head; iter; + iter = g_list_next (iter)) { + GstValidateOverride *override = iter->data; + + gst_validate_override_event_handler (override, + GST_VALIDATE_MONITOR_CAST (pad_monitor), event); + } + GST_VALIDATE_MONITOR_OVERRIDES_UNLOCK (pad_monitor); +} + +static void +gst_validate_pad_monitor_buffer_overrides (GstValidatePadMonitor * pad_monitor, + GstBuffer * buffer) +{ + GList *iter; + + GST_VALIDATE_MONITOR_OVERRIDES_LOCK (pad_monitor); + for (iter = GST_VALIDATE_MONITOR_OVERRIDES (pad_monitor).head; iter; + iter = g_list_next (iter)) { + GstValidateOverride *override = iter->data; + + gst_validate_override_buffer_handler (override, + GST_VALIDATE_MONITOR_CAST (pad_monitor), buffer); + } + GST_VALIDATE_MONITOR_OVERRIDES_UNLOCK (pad_monitor); +} + +static void +gst_validate_pad_monitor_buffer_probe_overrides (GstValidatePadMonitor * + pad_monitor, GstBuffer * buffer) +{ + GList *iter; + + GST_VALIDATE_MONITOR_OVERRIDES_LOCK (pad_monitor); + for (iter = GST_VALIDATE_MONITOR_OVERRIDES (pad_monitor).head; iter; + iter = g_list_next (iter)) { + GstValidateOverride *override = iter->data; + + gst_validate_override_buffer_probe_handler (override, + GST_VALIDATE_MONITOR_CAST (pad_monitor), buffer); + } + GST_VALIDATE_MONITOR_OVERRIDES_UNLOCK (pad_monitor); +} + +static void +gst_validate_pad_monitor_query_overrides (GstValidatePadMonitor * pad_monitor, + GstQuery * query) +{ + GList *iter; + + GST_VALIDATE_MONITOR_OVERRIDES_LOCK (pad_monitor); + for (iter = GST_VALIDATE_MONITOR_OVERRIDES (pad_monitor).head; iter; + iter = g_list_next (iter)) { + GstValidateOverride *override = iter->data; + + gst_validate_override_query_handler (override, + GST_VALIDATE_MONITOR_CAST (pad_monitor), query); + } + GST_VALIDATE_MONITOR_OVERRIDES_UNLOCK (pad_monitor); +} + +static void +gst_validate_pad_monitor_setcaps_overrides (GstValidatePadMonitor * pad_monitor, + GstCaps * caps) +{ + GList *iter; + + GST_VALIDATE_MONITOR_OVERRIDES_LOCK (pad_monitor); + for (iter = GST_VALIDATE_MONITOR_OVERRIDES (pad_monitor).head; iter; + iter = g_list_next (iter)) { + GstValidateOverride *override = iter->data; + + gst_validate_override_setcaps_handler (override, + GST_VALIDATE_MONITOR_CAST (pad_monitor), caps); + } + GST_VALIDATE_MONITOR_OVERRIDES_UNLOCK (pad_monitor); +} + +/* FIXME : This is a bit dubious, what's the point of this check ? */ +static gboolean +gst_validate_pad_monitor_timestamp_is_in_received_range (GstValidatePadMonitor * + monitor, GstClockTime ts, GstClockTime tolerance) +{ + GST_DEBUG_OBJECT (monitor->pad, "Checking if timestamp %" GST_TIME_FORMAT + " is in range: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT " for pad " + "%s:%s with tolerance: %" GST_TIME_FORMAT, GST_TIME_ARGS (ts), + GST_TIME_ARGS (monitor->timestamp_range_start), + GST_TIME_ARGS (monitor->timestamp_range_end), + GST_DEBUG_PAD_NAME (GST_VALIDATE_PAD_MONITOR_GET_PAD (monitor)), + GST_TIME_ARGS (tolerance)); + return !GST_CLOCK_TIME_IS_VALID (monitor->timestamp_range_start) || + !GST_CLOCK_TIME_IS_VALID (monitor->timestamp_range_end) || + ((monitor->timestamp_range_start >= tolerance ? + monitor->timestamp_range_start - tolerance : 0) <= ts + && (ts >= tolerance ? ts - tolerance : 0) <= + monitor->timestamp_range_end); +} + +/* Iterates over internal links (sinkpads) to check that this buffer has + * a timestamp that is in the range of the lastly received buffers */ +static void + gst_validate_pad_monitor_check_buffer_timestamp_in_received_range + (GstValidatePadMonitor * monitor, GstBuffer * buffer, + GstClockTime tolerance) +{ + GstClockTime ts; + GstClockTime ts_end; + GstIterator *iter; + gboolean has_one = FALSE; + gboolean found = FALSE; + gboolean done; + GstPad *otherpad; + GstValidatePadMonitor *othermonitor; + + if (!GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (buffer)) + || !GST_CLOCK_TIME_IS_VALID (GST_BUFFER_DURATION (buffer))) { + GST_DEBUG_OBJECT (monitor->pad, + "Can't check buffer timestamps range as " + "buffer has no valid timestamp/duration"); + return; + } + ts = GST_BUFFER_TIMESTAMP (buffer); + ts_end = ts + GST_BUFFER_DURATION (buffer); + + iter = + gst_pad_iterate_internal_links (GST_VALIDATE_PAD_MONITOR_GET_PAD + (monitor)); + + if (iter == NULL) { + GST_WARNING_OBJECT (GST_VALIDATE_PAD_MONITOR_GET_PAD (monitor), + "No iterator available"); + return; + } + + done = FALSE; + while (!done) { + GValue value = { 0, }; + switch (gst_iterator_next (iter, &value)) { + case GST_ITERATOR_OK: + otherpad = g_value_get_object (&value); + GST_DEBUG_OBJECT (monitor->pad, "Checking pad %s:%s input timestamps", + GST_DEBUG_PAD_NAME (otherpad)); + othermonitor = + g_object_get_data ((GObject *) otherpad, "validate-monitor"); + GST_VALIDATE_MONITOR_LOCK (othermonitor); + if (gst_validate_pad_monitor_timestamp_is_in_received_range + (othermonitor, ts, tolerance) + && + gst_validate_pad_monitor_timestamp_is_in_received_range + (othermonitor, ts_end, tolerance)) { + done = TRUE; + found = TRUE; + } + GST_VALIDATE_MONITOR_UNLOCK (othermonitor); + g_value_reset (&value); + has_one = TRUE; + break; + case GST_ITERATOR_RESYNC: + gst_iterator_resync (iter); + has_one = FALSE; + found = FALSE; + break; + case GST_ITERATOR_ERROR: + GST_WARNING_OBJECT (monitor->pad, "Internal links pad iteration error"); + done = TRUE; + break; + case GST_ITERATOR_DONE: + done = TRUE; + break; + } + } + gst_iterator_free (iter); + + if (!has_one) { + GST_DEBUG_OBJECT (monitor->pad, "Skipping timestamp in range check as no " + "internal linked pad was found"); + return; + } + if (!found) { + GST_VALIDATE_REPORT (monitor, BUFFER_TIMESTAMP_OUT_OF_RECEIVED_RANGE, + "Timestamp %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT + " is out of range of received input", GST_TIME_ARGS (ts), + GST_TIME_ARGS (ts_end)); + } +} + +static void +gst_validate_pad_monitor_check_first_buffer (GstValidatePadMonitor * + pad_monitor, GstBuffer * buffer) +{ + if (G_UNLIKELY (pad_monitor->first_buffer)) { + pad_monitor->first_buffer = FALSE; + + if (!pad_monitor->has_segment + && PAD_IS_IN_PUSH_MODE (GST_VALIDATE_PAD_MONITOR_GET_PAD (pad_monitor))) + { + GST_VALIDATE_REPORT (pad_monitor, BUFFER_BEFORE_SEGMENT, + "Received buffer before Segment event"); + } + + GST_DEBUG_OBJECT (pad_monitor->pad, + "Checking first buffer (pts:%" GST_TIME_FORMAT " dts:%" GST_TIME_FORMAT + ")", GST_TIME_ARGS (GST_BUFFER_PTS (buffer)), + GST_TIME_ARGS (GST_BUFFER_DTS (buffer))); + + } +} + +static void +gst_validate_pad_monitor_check_eos (GstValidatePadMonitor * + pad_monitor, GstBuffer * buffer) +{ + if (G_UNLIKELY (pad_monitor->is_eos)) { + GST_VALIDATE_REPORT (pad_monitor, BUFFER_AFTER_EOS, + "Received buffer %" GST_PTR_FORMAT " after EOS", buffer); + } +} + +static void +gst_validate_pad_monitor_update_buffer_data (GstValidatePadMonitor * + pad_monitor, GstBuffer * buffer) +{ + pad_monitor->current_timestamp = GST_BUFFER_TIMESTAMP (buffer); + pad_monitor->current_duration = GST_BUFFER_DURATION (buffer); + if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (buffer))) { + if (GST_CLOCK_TIME_IS_VALID (pad_monitor->timestamp_range_start)) { + pad_monitor->timestamp_range_start = + MIN (pad_monitor->timestamp_range_start, + GST_BUFFER_TIMESTAMP (buffer)); + } else { + pad_monitor->timestamp_range_start = GST_BUFFER_TIMESTAMP (buffer); + } + + if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_DURATION (buffer))) { + GstClockTime endts = + GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer); + if (GST_CLOCK_TIME_IS_VALID (pad_monitor->timestamp_range_end)) { + pad_monitor->timestamp_range_end = + MAX (pad_monitor->timestamp_range_end, endts); + } else { + pad_monitor->timestamp_range_end = endts; + } + } + } + GST_DEBUG_OBJECT (pad_monitor->pad, "Current stored range: %" GST_TIME_FORMAT + " - %" GST_TIME_FORMAT, + GST_TIME_ARGS (pad_monitor->timestamp_range_start), + GST_TIME_ARGS (pad_monitor->timestamp_range_end)); +} + +static GstFlowReturn +_combine_flows (GstFlowReturn ret1, GstFlowReturn ret2) +{ + if (ret1 == ret2) + return ret1; + if (ret1 <= GST_FLOW_NOT_NEGOTIATED) + return ret1; + if (ret2 <= GST_FLOW_NOT_NEGOTIATED) + return ret2; + if (ret1 == GST_FLOW_FLUSHING || ret2 == GST_FLOW_FLUSHING) + return GST_FLOW_FLUSHING; + if (ret1 == GST_FLOW_OK || ret2 == GST_FLOW_OK) + return GST_FLOW_OK; + return ret2; +} + +static void +gst_validate_pad_monitor_check_aggregated_return (GstValidatePadMonitor * + monitor, GstFlowReturn ret) +{ + GstIterator *iter; + gboolean done; + GstPad *otherpad; + GstPad *peerpad; + GstValidatePadMonitor *othermonitor; + GstFlowReturn aggregated = GST_FLOW_NOT_LINKED; + gboolean found_a_pad = FALSE; + GstPad *pad = GST_VALIDATE_PAD_MONITOR_GET_PAD (monitor); + + iter = gst_pad_iterate_internal_links (pad); + done = FALSE; + while (!done) { + GValue value = { 0, }; + switch (gst_iterator_next (iter, &value)) { + case GST_ITERATOR_OK: + otherpad = g_value_get_object (&value); + peerpad = gst_pad_get_peer (otherpad); + if (peerpad) { + othermonitor = + g_object_get_data ((GObject *) peerpad, "validate-monitor"); + if (othermonitor) { + found_a_pad = TRUE; + GST_VALIDATE_MONITOR_LOCK (othermonitor); + aggregated = + _combine_flows (aggregated, othermonitor->last_flow_return); + GST_VALIDATE_MONITOR_UNLOCK (othermonitor); + } + + gst_object_unref (peerpad); + } + g_value_reset (&value); + break; + case GST_ITERATOR_RESYNC: + gst_iterator_resync (iter); + break; + case GST_ITERATOR_ERROR: + GST_WARNING_OBJECT (monitor->pad, "Internal links pad iteration error"); + done = TRUE; + break; + case GST_ITERATOR_DONE: + done = TRUE; + break; + } + } + gst_iterator_free (iter); + if (!found_a_pad) { + /* no peer pad found, nothing to do */ + return; + } + if (aggregated == GST_FLOW_OK || aggregated == GST_FLOW_EOS) { + /* those are acceptable situations */ + + if (GST_PAD_IS_FLUSHING (pad) && ret == GST_FLOW_FLUSHING) { + /* pad is flushing, always acceptable to return flushing */ + return; + } + + if (monitor->is_eos && ret == GST_FLOW_EOS) { + /* this element received eos and returned eos */ + return; + } + + if (PAD_PARENT_IS_DEMUXER (monitor) && ret == GST_FLOW_EOS) { + /* a demuxer can return EOS when the samples end */ + return; + } + } + + if (aggregated != ret) { + GST_VALIDATE_REPORT (monitor, WRONG_FLOW_RETURN, + "Wrong combined flow return %s(%d). Expected: %s(%d)", + gst_flow_get_name (ret), ret, gst_flow_get_name (aggregated), + aggregated); + } +} + +static void + gst_validate_pad_monitor_otherpad_add_pending_serialized_event + (GstValidatePadMonitor * monitor, GstEvent * event, GstClockTime last_ts) +{ + GstIterator *iter; + gboolean done; + GstPad *otherpad; + GstValidatePadMonitor *othermonitor; + + if (!GST_EVENT_IS_SERIALIZED (event)) + return; + + iter = + gst_pad_iterate_internal_links (GST_VALIDATE_PAD_MONITOR_GET_PAD + (monitor)); + if (iter == NULL) { + /* inputselector will return NULL if the sinkpad is not the active one .... */ + GST_FIXME_OBJECT (GST_VALIDATE_PAD_MONITOR_GET_PAD + (monitor), "No iterator"); + return; + } + done = FALSE; + while (!done) { + GValue value = { 0, }; + switch (gst_iterator_next (iter, &value)) { + case GST_ITERATOR_OK: + otherpad = g_value_get_object (&value); + othermonitor = + g_object_get_data ((GObject *) otherpad, "validate-monitor"); + if (othermonitor) { + SerializedEventData *data = g_slice_new0 (SerializedEventData); + data->timestamp = last_ts; + data->event = gst_event_ref (event); + GST_VALIDATE_MONITOR_LOCK (othermonitor); + GST_DEBUG_OBJECT (monitor->pad, "Storing for pad %s:%s event %p %s", + GST_DEBUG_PAD_NAME (otherpad), event, + GST_EVENT_TYPE_NAME (event)); + g_ptr_array_add (othermonitor->serialized_events, data); + debug_pending_event (otherpad, othermonitor->serialized_events); + GST_VALIDATE_MONITOR_UNLOCK (othermonitor); + } + g_value_reset (&value); + break; + case GST_ITERATOR_RESYNC: + gst_iterator_resync (iter); + break; + case GST_ITERATOR_ERROR: + GST_WARNING_OBJECT (monitor->pad, "Internal links pad iteration error"); + done = TRUE; + break; + case GST_ITERATOR_DONE: + done = TRUE; + break; + } + } + gst_iterator_free (iter); +} + +static void +gst_validate_pad_monitor_otherpad_add_pending_field (GstValidatePadMonitor * + monitor, GstStructure * structure, const gchar * field) +{ + GstIterator *iter; + gboolean done; + GstPad *otherpad; + GstValidatePadMonitor *othermonitor; + const GValue *v; + + v = gst_structure_get_value (structure, field); + if (v == NULL) { + GST_DEBUG_OBJECT (monitor->pad, "Not adding pending field %s as it isn't " + "present on structure %" GST_PTR_FORMAT, field, structure); + return; + } + + iter = + gst_pad_iterate_internal_links (GST_VALIDATE_PAD_MONITOR_GET_PAD + (monitor)); + done = FALSE; + while (!done) { + GValue value = { 0, }; + switch (gst_iterator_next (iter, &value)) { + case GST_ITERATOR_OK: + otherpad = g_value_get_object (&value); + othermonitor = + g_object_get_data ((GObject *) otherpad, "validate-monitor"); + if (othermonitor) { + GST_VALIDATE_MONITOR_LOCK (othermonitor); + g_assert (othermonitor->pending_setcaps_fields != NULL); + gst_structure_set_value (othermonitor->pending_setcaps_fields, + field, v); + GST_VALIDATE_MONITOR_UNLOCK (othermonitor); + } + g_value_reset (&value); + break; + case GST_ITERATOR_RESYNC: + gst_iterator_resync (iter); + break; + case GST_ITERATOR_ERROR: + GST_WARNING_OBJECT (monitor->pad, "Internal links pad iteration error"); + done = TRUE; + break; + case GST_ITERATOR_DONE: + done = TRUE; + break; + } + } + gst_iterator_free (iter); +} + +static void +gst_validate_pad_monitor_otherpad_clear_pending_fields (GstValidatePadMonitor * + monitor) +{ + GstIterator *iter; + gboolean done; + GstPad *otherpad; + GstValidatePadMonitor *othermonitor; + + iter = + gst_pad_iterate_internal_links (GST_VALIDATE_PAD_MONITOR_GET_PAD + (monitor)); + + if (iter == NULL) { + GST_DEBUG_OBJECT (monitor, "No internally linked pad"); + + return; + } + + done = FALSE; + while (!done) { + GValue value = { 0, }; + switch (gst_iterator_next (iter, &value)) { + case GST_ITERATOR_OK: + otherpad = g_value_get_object (&value); + othermonitor = + g_object_get_data ((GObject *) otherpad, "validate-monitor"); + if (othermonitor) { + GST_VALIDATE_MONITOR_LOCK (othermonitor); + g_assert (othermonitor->pending_setcaps_fields != NULL); + gst_structure_free (othermonitor->pending_setcaps_fields); + othermonitor->pending_setcaps_fields = + gst_structure_new_empty (PENDING_FIELDS); + GST_VALIDATE_MONITOR_UNLOCK (othermonitor); + } + g_value_reset (&value); + break; + case GST_ITERATOR_RESYNC: + gst_iterator_resync (iter); + break; + case GST_ITERATOR_ERROR: + GST_WARNING_OBJECT (monitor->pad, "Internal links pad iteration error"); + done = TRUE; + break; + case GST_ITERATOR_DONE: + done = TRUE; + break; + } + } + gst_iterator_free (iter); +} + +static void +gst_validate_pad_monitor_add_expected_newsegment (GstValidatePadMonitor * + monitor, GstEvent * event) +{ + GstIterator *iter; + gboolean done; + GstPad *otherpad; + GstValidatePadMonitor *othermonitor; + + iter = + gst_pad_iterate_internal_links (GST_VALIDATE_PAD_MONITOR_GET_PAD + (monitor)); + + if (iter == NULL) { + GST_DEBUG_OBJECT (monitor, "No internally linked pad"); + return; + } + + done = FALSE; + while (!done) { + GValue value = { 0, }; + switch (gst_iterator_next (iter, &value)) { + case GST_ITERATOR_OK: + otherpad = g_value_get_object (&value); + if (!otherpad) + continue; + othermonitor = + g_object_get_data ((GObject *) otherpad, "validate-monitor"); + GST_VALIDATE_MONITOR_LOCK (othermonitor); + gst_event_replace (&othermonitor->expected_segment, event); + GST_VALIDATE_MONITOR_UNLOCK (othermonitor); + g_value_reset (&value); + break; + case GST_ITERATOR_RESYNC: + gst_iterator_resync (iter); + break; + case GST_ITERATOR_ERROR: + GST_WARNING_OBJECT (monitor->pad, "Internal links pad iteration error"); + done = TRUE; + break; + case GST_ITERATOR_DONE: + done = TRUE; + break; + } + } + gst_iterator_free (iter); +} + +static void +gst_validate_pad_monitor_flush (GstValidatePadMonitor * pad_monitor) +{ + pad_monitor->current_timestamp = GST_CLOCK_TIME_NONE; + pad_monitor->current_duration = GST_CLOCK_TIME_NONE; + pad_monitor->timestamp_range_start = GST_CLOCK_TIME_NONE; + pad_monitor->timestamp_range_end = GST_CLOCK_TIME_NONE; + pad_monitor->has_segment = FALSE; + pad_monitor->is_eos = FALSE; + pad_monitor->last_flow_return = GST_FLOW_OK; + gst_caps_replace (&pad_monitor->last_caps, NULL); + pad_monitor->caps_is_audio = pad_monitor->caps_is_video = + pad_monitor->caps_is_raw = FALSE; + + g_list_free_full (pad_monitor->expired_events, + (GDestroyNotify) gst_event_unref); + pad_monitor->expired_events = NULL; + + if (pad_monitor->serialized_events->len) + g_ptr_array_remove_range (pad_monitor->serialized_events, 0, + pad_monitor->serialized_events->len); +} + +/* common checks for both sink and src event functions */ +static void +gst_validate_pad_monitor_common_event_check (GstValidatePadMonitor * + pad_monitor, GstEvent * event) +{ + guint32 seqnum = gst_event_get_seqnum (event); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_START: + { + if (pad_monitor->pending_flush_start_seqnum) { + if (seqnum == pad_monitor->pending_flush_start_seqnum) { + pad_monitor->pending_flush_start_seqnum = 0; + } else { + GST_VALIDATE_REPORT (pad_monitor, FLUSH_START_HAS_WRONG_SEQNUM, + "Got: %u Expected: %u", seqnum, + pad_monitor->pending_flush_start_seqnum); + } + } + + if (pad_monitor->pending_flush_stop) { + GST_VALIDATE_REPORT (pad_monitor, EVENT_FLUSH_START_UNEXPECTED, + "Received flush-start from " " when flush-stop was expected"); + } + pad_monitor->pending_flush_stop = TRUE; + } + break; + case GST_EVENT_FLUSH_STOP: + { + if (pad_monitor->pending_flush_stop_seqnum) { + if (seqnum == pad_monitor->pending_flush_stop_seqnum) { + pad_monitor->pending_flush_stop_seqnum = 0; + } else { + GST_VALIDATE_REPORT (pad_monitor, FLUSH_STOP_HAS_WRONG_SEQNUM, + "Got: %u Expected: %u", seqnum, + pad_monitor->pending_flush_stop_seqnum); + } + } + + pad_monitor->pending_newsegment_seqnum = seqnum; + pad_monitor->pending_eos_seqnum = seqnum; + + if (!pad_monitor->pending_flush_stop) { + gchar *event_str = _get_event_string (event); + + GST_VALIDATE_REPORT (pad_monitor, EVENT_FLUSH_STOP_UNEXPECTED, + "Unexpected flush-stop %s", event_str); + g_free (event_str); + } + pad_monitor->pending_flush_stop = FALSE; + + /* cleanup our data */ + gst_validate_pad_monitor_flush (pad_monitor); + } + break; + default: + break; + } +} + +static void +mark_pads_eos (GstValidatePadMonitor * pad_monitor) +{ + GstValidatePadMonitor *peer_monitor; + GstPad *peer = gst_pad_get_peer (pad_monitor->pad); + GstPad *real_peer; + + pad_monitor->is_eos = TRUE; + if (peer) { + real_peer = _get_actual_pad (peer); + peer_monitor = + g_object_get_data ((GObject *) real_peer, "validate-monitor"); + if (peer_monitor) + peer_monitor->is_eos = TRUE; + gst_object_unref (peer); + gst_object_unref (real_peer); + } +} + +static inline gboolean +_should_check_buffers (GstValidatePadMonitor * pad_monitor, + gboolean force_checks) +{ + GstPad *pad = GST_VALIDATE_PAD_MONITOR_GET_PAD (pad_monitor); + GstValidateMonitor *monitor = GST_VALIDATE_MONITOR (pad_monitor); + + if (pad_monitor->first_buffer || force_checks) { + if (pad_monitor->segment.rate != 1.0) { + GST_INFO_OBJECT (pad_monitor, "We do not support buffer checking" + " for trick modes"); + + pad_monitor->check_buffers = FALSE; + } else if (!PAD_PARENT_IS_DECODER (pad_monitor)) { + GST_DEBUG_OBJECT (pad, "Not on a decoder => no buffer checking"); + + pad_monitor->check_buffers = FALSE; + } else if (GST_PAD_DIRECTION (pad) != GST_PAD_SINK) { + GST_DEBUG_OBJECT (pad, "Not a sinkpad => no buffer checking"); + + pad_monitor->check_buffers = FALSE; + } else if (!pad_monitor->caps_is_video) { + GST_DEBUG_OBJECT (pad, "Not working with video => no buffer checking"); + + pad_monitor->check_buffers = FALSE; + } else if (monitor->media_descriptor == NULL) { + GST_DEBUG_OBJECT (pad, "No media_descriptor set => no buffer checking"); + + pad_monitor->check_buffers = FALSE; + } else if (!gst_media_descriptor_detects_frames (monitor->media_descriptor)) { + GST_DEBUG_OBJECT (pad, "No frame detection media descriptor " + "=> not buffer checking"); + pad_monitor->check_buffers = FALSE; + } else if (pad_monitor->all_bufs == NULL && + !gst_media_descriptor_get_buffers (monitor->media_descriptor, pad, NULL, + &pad_monitor->all_bufs)) { + + GST_INFO_OBJECT (monitor, + "The MediaInfo is marked as detecting frame, but getting frames" + " from pad %" GST_PTR_FORMAT " did not work (some format conversion" + " might be happening)", pad); + + pad_monitor->check_buffers = FALSE; + } else { + if (!pad_monitor->current_buf) + pad_monitor->current_buf = pad_monitor->all_bufs; + pad_monitor->check_buffers = TRUE; + } + } + + return pad_monitor->check_buffers; +} + +static void +gst_validate_monitor_find_next_buffer (GstValidatePadMonitor * pad_monitor) +{ + GList *tmp; + gboolean passed_start = FALSE; + + if (!_should_check_buffers (pad_monitor, TRUE)) + return; + + for (tmp = g_list_last (pad_monitor->all_bufs); tmp; tmp = tmp->prev) { + GstBuffer *cbuf = tmp->data; + GstClockTime ts = + GST_CLOCK_TIME_IS_VALID (GST_BUFFER_DTS (cbuf)) ? GST_BUFFER_DTS (cbuf) + : GST_BUFFER_PTS (cbuf); + + if (!GST_CLOCK_TIME_IS_VALID (ts)) + continue; + + if (ts <= pad_monitor->segment.start) + passed_start = TRUE; + + if (!passed_start) + continue; + + if (!GST_BUFFER_FLAG_IS_SET (cbuf, GST_BUFFER_FLAG_DELTA_UNIT)) { + break; + } + } + + if (tmp == NULL) + pad_monitor->current_buf = pad_monitor->all_bufs; + else + pad_monitor->current_buf = tmp; +} + +static gboolean +gst_validate_pad_monitor_downstream_event_check (GstValidatePadMonitor * + pad_monitor, GstObject * parent, GstEvent * event, + GstPadEventFunction handler) +{ + gboolean ret = TRUE; + const GstSegment *segment; + guint32 seqnum = gst_event_get_seqnum (event); + GstPad *pad = GST_VALIDATE_PAD_MONITOR_GET_PAD (pad_monitor); + + gst_validate_pad_monitor_common_event_check (pad_monitor, event); + + /* pre checks */ + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEGMENT: + /* parse segment data to be used if event is handled */ + gst_event_parse_segment (event, &segment); + + GST_DEBUG_OBJECT (pad_monitor->pad, "Got segment %" GST_SEGMENT_FORMAT, + segment); + + if (pad_monitor->pending_newsegment_seqnum) { + if (pad_monitor->pending_newsegment_seqnum == seqnum) { + pad_monitor->pending_newsegment_seqnum = 0; + } else { + GST_VALIDATE_REPORT (pad_monitor, SEGMENT_HAS_WRONG_SEQNUM, + "Got: %u Expected: %u", seqnum, pad_monitor->pending_eos_seqnum); + } + } + + pad_monitor->pending_eos_seqnum = seqnum; + + if (GST_PAD_DIRECTION (pad) == GST_PAD_SINK) { + gst_validate_pad_monitor_add_expected_newsegment (pad_monitor, event); + } else { + /* check if this segment is the expected one */ + if (pad_monitor->expected_segment) { + const GstSegment *exp_segment; + + if (pad_monitor->expected_segment != event) { + gst_event_parse_segment (pad_monitor->expected_segment, + &exp_segment); + if (segment->format == exp_segment->format) { + if ((exp_segment->rate * exp_segment->applied_rate != + segment->rate * segment->applied_rate)) + GST_VALIDATE_REPORT (pad_monitor, EVENT_NEW_SEGMENT_MISMATCH, + "Rate * applied_rate %f != expected %f", + segment->rate * segment->applied_rate, + exp_segment->rate * exp_segment->applied_rate); + if (exp_segment->start != segment->start) + GST_VALIDATE_REPORT (pad_monitor, EVENT_NEW_SEGMENT_MISMATCH, + "Start %" GST_TIME_FORMAT " != expected %" GST_TIME_FORMAT, + GST_TIME_ARGS (segment->start), + GST_TIME_ARGS (exp_segment->start)); + if (exp_segment->stop != segment->stop) + GST_VALIDATE_REPORT (pad_monitor, EVENT_NEW_SEGMENT_MISMATCH, + "Stop %" GST_TIME_FORMAT " != expected %" GST_TIME_FORMAT, + GST_TIME_ARGS (segment->stop), + GST_TIME_ARGS (exp_segment->stop)); + if (exp_segment->position != segment->position) + GST_VALIDATE_REPORT (pad_monitor, EVENT_NEW_SEGMENT_MISMATCH, + "Position %" GST_TIME_FORMAT " != expected %" + GST_TIME_FORMAT, GST_TIME_ARGS (segment->position), + GST_TIME_ARGS (exp_segment->position)); + } + } + gst_event_replace (&pad_monitor->expected_segment, NULL); + } + } + break; + case GST_EVENT_CAPS:{ + GstCaps *caps; + + gst_event_parse_caps (event, &caps); + gst_validate_pad_monitor_setcaps_pre (pad_monitor, caps); + break; + } + case GST_EVENT_EOS: + pad_monitor->is_eos = TRUE; + if (pad_monitor->pending_eos_seqnum == 0) { + GST_VALIDATE_REPORT (pad_monitor, EVENT_EOS_WITHOUT_SEGMENT, + "EOS %" GST_PTR_FORMAT " received before a segment was received", + event); + } else if (pad_monitor->pending_eos_seqnum != seqnum) { + GST_VALIDATE_REPORT (pad_monitor, EOS_HAS_WRONG_SEQNUM, + "Got: %u. Expected: %u", seqnum, pad_monitor->pending_eos_seqnum); + } + + /* + * TODO add end of stream checks for + * - events not pushed + * - buffer data not pushed + * - pending events not received + */ + break; + + /* both flushes are handled by the common event function */ + case GST_EVENT_FLUSH_START: + case GST_EVENT_FLUSH_STOP: + case GST_EVENT_TAG: + case GST_EVENT_SINK_MESSAGE: + default: + break; + } + + GST_VALIDATE_MONITOR_UNLOCK (pad_monitor); + GST_VALIDATE_PAD_MONITOR_PARENT_UNLOCK (pad_monitor); + gst_validate_pad_monitor_event_overrides (pad_monitor, event); + if (handler) { + gst_event_ref (event); + ret = pad_monitor->event_func (pad, parent, event); + } + GST_VALIDATE_PAD_MONITOR_PARENT_LOCK (pad_monitor); + GST_VALIDATE_MONITOR_LOCK (pad_monitor); + + /* post checks */ + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEGMENT: + if (ret) { + if (!pad_monitor->has_segment + && pad_monitor->segment.format != segment->format) { + gst_segment_init (&pad_monitor->segment, segment->format); + } + gst_segment_copy_into (segment, &pad_monitor->segment); + pad_monitor->has_segment = TRUE; + gst_validate_monitor_find_next_buffer (pad_monitor); + } + break; + case GST_EVENT_CAPS:{ + GstCaps *caps; + + gst_event_parse_caps (event, &caps); + gst_validate_pad_monitor_setcaps_post (pad_monitor, caps, ret); + break; + } + case GST_EVENT_FLUSH_START: + case GST_EVENT_FLUSH_STOP: + case GST_EVENT_EOS: + case GST_EVENT_TAG: + case GST_EVENT_SINK_MESSAGE: + default: + break; + } + + if (handler) + gst_event_unref (event); + return ret; +} + +static gboolean +gst_validate_pad_monitor_src_event_check (GstValidatePadMonitor * pad_monitor, + GstObject * parent, GstEvent * event, GstPadEventFunction handler) +{ + gboolean ret = TRUE; + gdouble rate; + GstFormat format; + gint64 start, stop; + GstSeekFlags seek_flags; + GstSeekType start_type, stop_type; + guint32 seqnum = gst_event_get_seqnum (event); + GstPad *pad = GST_VALIDATE_PAD_MONITOR_GET_PAD (pad_monitor); + + gst_validate_pad_monitor_common_event_check (pad_monitor, event); + + /* pre checks */ + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEEK: + { + gst_event_parse_seek (event, &rate, &format, &seek_flags, &start_type, + &start, &stop_type, &stop); + /* upstream seek - store the seek event seqnum to check + * flushes and newsegments share the same */ + + /* TODO we might need to use a list as multiple seeks can be sent + * before the flushes arrive here */ + if (seek_flags & GST_SEEK_FLAG_FLUSH) { + pad_monitor->pending_flush_start_seqnum = seqnum; + pad_monitor->pending_flush_stop_seqnum = seqnum; + } + } + break; + /* both flushes are handled by the common event handling function */ + case GST_EVENT_FLUSH_START: + case GST_EVENT_FLUSH_STOP: + case GST_EVENT_NAVIGATION: + case GST_EVENT_LATENCY: + case GST_EVENT_STEP: + case GST_EVENT_QOS: + default: + break; + } + + if (handler) { + GST_VALIDATE_MONITOR_UNLOCK (pad_monitor); + gst_event_ref (event); + ret = pad_monitor->event_func (pad, parent, event); + GST_VALIDATE_MONITOR_LOCK (pad_monitor); + } + + /* post checks */ + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_START: + case GST_EVENT_FLUSH_STOP: + case GST_EVENT_QOS: + case GST_EVENT_SEEK: + { + if (ret == FALSE) { + /* do not expect any of these events anymore */ + pad_monitor->pending_flush_start_seqnum = 0; + pad_monitor->pending_flush_stop_seqnum = 0; + pad_monitor->pending_newsegment_seqnum = 0; + pad_monitor->pending_eos_seqnum = 0; + } + } + break; + case GST_EVENT_NAVIGATION: + case GST_EVENT_LATENCY: + case GST_EVENT_STEP: + default: + break; + } + + if (handler) + gst_event_unref (event); + return ret; +} + +static gboolean +gst_validate_pad_monitor_check_right_buffer (GstValidatePadMonitor * + pad_monitor, GstBuffer * buffer) +{ + gchar *checksum; + GstBuffer *wanted_buf; + GstMapInfo map, wanted_map; + + gboolean ret = TRUE; + GstPad *pad = GST_VALIDATE_PAD_MONITOR_GET_PAD (pad_monitor); + + if (_should_check_buffers (pad_monitor, FALSE) == FALSE) + return FALSE; + + if (pad_monitor->current_buf == NULL) { + GST_INFO_OBJECT (pad, "No current buffer one pad, Why?"); + return FALSE; + } + + wanted_buf = pad_monitor->current_buf->data; + + if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_PTS (wanted_buf)) && + GST_CLOCK_TIME_IS_VALID (GST_BUFFER_PTS (buffer)) && + GST_BUFFER_PTS (wanted_buf) != GST_BUFFER_PTS (buffer)) { + + GST_VALIDATE_REPORT (pad_monitor, WRONG_BUFFER, + "buffer %" GST_PTR_FORMAT " PTS %" GST_TIME_FORMAT + " different than expected: %" GST_TIME_FORMAT, buffer, + GST_TIME_ARGS (GST_BUFFER_PTS (buffer)), + GST_TIME_ARGS (GST_BUFFER_PTS (wanted_buf))); + + ret = FALSE; + } + + if (GST_BUFFER_DTS (wanted_buf) != GST_BUFFER_DTS (buffer)) { + GST_VALIDATE_REPORT (pad_monitor, WRONG_BUFFER, + "buffer %" GST_PTR_FORMAT " DTS %" GST_TIME_FORMAT + " different than expected: %" GST_TIME_FORMAT, buffer, + GST_TIME_ARGS (GST_BUFFER_DTS (buffer)), + GST_TIME_ARGS (GST_BUFFER_DTS (wanted_buf))); + ret = FALSE; + } + + if (GST_BUFFER_DURATION (wanted_buf) != GST_BUFFER_DURATION (buffer)) { + GST_VALIDATE_REPORT (pad_monitor, WRONG_BUFFER, + "buffer %" GST_PTR_FORMAT " DURATION %" GST_TIME_FORMAT + " different than expected: %" GST_TIME_FORMAT, buffer, + GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)), + GST_TIME_ARGS (GST_BUFFER_DURATION (wanted_buf))); + ret = FALSE; + } + + if (GST_BUFFER_FLAG_IS_SET (wanted_buf, GST_BUFFER_FLAG_DELTA_UNIT) != + GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT)) { + GST_VALIDATE_REPORT (pad_monitor, WRONG_BUFFER, + "buffer %" GST_PTR_FORMAT " Delta unit is set to %s but expected %s", + buffer, GST_BUFFER_FLAG_IS_SET (buffer, + GST_BUFFER_FLAG_DELTA_UNIT) ? "True" : "False", + GST_BUFFER_FLAG_IS_SET (wanted_buf, + GST_BUFFER_FLAG_DELTA_UNIT) ? "True" : "False"); + ret = FALSE; + } + + g_assert (gst_buffer_map (wanted_buf, &wanted_map, GST_MAP_READ)); + g_assert (gst_buffer_map (buffer, &map, GST_MAP_READ)); + + checksum = g_compute_checksum_for_data (G_CHECKSUM_MD5, + (const guchar *) map.data, map.size); + + if (g_strcmp0 ((gchar *) wanted_map.data, checksum)) { + GST_VALIDATE_REPORT (pad_monitor, WRONG_BUFFER, + "buffer %" GST_PTR_FORMAT " checksum %s different from expected: %s", + buffer, checksum, wanted_map.data); + ret = FALSE; + } + + gst_buffer_unmap (wanted_buf, &wanted_map); + gst_buffer_unmap (buffer, &map); + g_free (checksum); + + pad_monitor->current_buf = pad_monitor->current_buf->next; + + return ret; +} + +static void +gst_validate_pad_monitor_check_return (GstValidatePadMonitor * pad_monitor, + GstFlowReturn ret) +{ + GstValidateMonitor *parent = GST_VALIDATE_MONITOR (pad_monitor); + + if (ret != GST_FLOW_ERROR) + return; + + while (GST_VALIDATE_MONITOR_GET_PARENT (parent)) + parent = GST_VALIDATE_MONITOR_GET_PARENT (parent); + + if (GST_IS_VALIDATE_PIPELINE_MONITOR (parent)) { + GstValidatePipelineMonitor *m = GST_VALIDATE_PIPELINE_MONITOR (parent); + + GST_VALIDATE_MONITOR_LOCK (m); + if (m->got_error == FALSE) { + GST_VALIDATE_REPORT (pad_monitor, FLOW_ERROR_WITHOUT_ERROR_MESSAGE, + "Pad return GST_FLOW_ERROR but no GST_MESSAGE_ERROR was received on" + " the bus"); + + /* Only report it the first time */ + m->got_error = TRUE; + } + GST_VALIDATE_MONITOR_UNLOCK (m); + } +} + +static GstFlowReturn +gst_validate_pad_monitor_chain_func (GstPad * pad, GstObject * parent, + GstBuffer * buffer) +{ + GstValidatePadMonitor *pad_monitor = + g_object_get_data ((GObject *) pad, "validate-monitor"); + GstFlowReturn ret; + + GST_VALIDATE_PAD_MONITOR_PARENT_LOCK (pad_monitor); + GST_VALIDATE_MONITOR_LOCK (pad_monitor); + + gst_validate_pad_monitor_check_right_buffer (pad_monitor, buffer); + gst_validate_pad_monitor_check_first_buffer (pad_monitor, buffer); + gst_validate_pad_monitor_update_buffer_data (pad_monitor, buffer); + gst_validate_pad_monitor_check_eos (pad_monitor, buffer); + + GST_VALIDATE_MONITOR_UNLOCK (pad_monitor); + GST_VALIDATE_PAD_MONITOR_PARENT_UNLOCK (pad_monitor); + + gst_validate_pad_monitor_buffer_overrides (pad_monitor, buffer); + + ret = pad_monitor->chain_func (pad, parent, buffer); + + gst_validate_pad_monitor_check_return (pad_monitor, ret); + + GST_VALIDATE_PAD_MONITOR_PARENT_LOCK (pad_monitor); + GST_VALIDATE_MONITOR_LOCK (pad_monitor); + + pad_monitor->last_flow_return = ret; + if (ret == GST_FLOW_EOS) { + mark_pads_eos (pad_monitor); + } + if (PAD_PARENT_IS_DEMUXER (pad_monitor)) + gst_validate_pad_monitor_check_aggregated_return (pad_monitor, ret); + + GST_VALIDATE_MONITOR_UNLOCK (pad_monitor); + GST_VALIDATE_PAD_MONITOR_PARENT_UNLOCK (pad_monitor); + + return ret; +} + +static gboolean +gst_validate_pad_monitor_event_is_tracked (GstValidatePadMonitor * monitor, + GstEvent * event) +{ + if (!GST_EVENT_IS_SERIALIZED (event)) { + return FALSE; + } + + /* we don't track Tag events because they mutate too much and it is hard + * to match a tag event pushed on a source pad with the one that was received + * on a sink pad. + * One idea would be to use seqnum, but it seems that it is undefined whether + * seqnums should be maintained in tag events that are created from others + * up to today. (2013-08-29) + */ + if (GST_EVENT_TYPE (event) == GST_EVENT_TAG) + return FALSE; + + return TRUE; +} + +static gboolean +gst_validate_pad_monitor_sink_event_func (GstPad * pad, GstObject * parent, + GstEvent * event) +{ + GstValidatePadMonitor *pad_monitor = + g_object_get_data ((GObject *) pad, "validate-monitor"); + gboolean ret; + + GST_VALIDATE_PAD_MONITOR_PARENT_LOCK (pad_monitor); + GST_VALIDATE_MONITOR_LOCK (pad_monitor); + + if (gst_validate_pad_monitor_event_is_tracked (pad_monitor, event)) { + GstClockTime last_ts = GST_CLOCK_TIME_NONE; + if (GST_CLOCK_TIME_IS_VALID (pad_monitor->current_timestamp)) { + last_ts = pad_monitor->current_timestamp; + if (GST_CLOCK_TIME_IS_VALID (pad_monitor->current_duration)) { + last_ts += pad_monitor->current_duration; + } + } + gst_validate_pad_monitor_otherpad_add_pending_serialized_event (pad_monitor, + event, last_ts); + } + + ret = + gst_validate_pad_monitor_downstream_event_check (pad_monitor, parent, + event, pad_monitor->event_func); + + GST_VALIDATE_MONITOR_UNLOCK (pad_monitor); + GST_VALIDATE_PAD_MONITOR_PARENT_UNLOCK (pad_monitor); + return ret; +} + +static gboolean +gst_validate_pad_monitor_src_event_func (GstPad * pad, GstObject * parent, + GstEvent * event) +{ + GstValidatePadMonitor *pad_monitor = + g_object_get_data ((GObject *) pad, "validate-monitor"); + gboolean ret; + + GST_VALIDATE_MONITOR_LOCK (pad_monitor); + ret = gst_validate_pad_monitor_src_event_check (pad_monitor, parent, event, + pad_monitor->event_func); + GST_VALIDATE_MONITOR_UNLOCK (pad_monitor); + return ret; +} + +static gboolean +gst_validate_pad_monitor_query_func (GstPad * pad, GstObject * parent, + GstQuery * query) +{ + GstValidatePadMonitor *pad_monitor = + g_object_get_data ((GObject *) pad, "validate-monitor"); + gboolean ret; + + gst_validate_pad_monitor_query_overrides (pad_monitor, query); + + ret = pad_monitor->query_func (pad, parent, query); + + if (ret) { + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_CAPS:{ + GstCaps *res; + GstCaps *filter; + + /* We shouldn't need to lock the parent as this doesn't modify + * other monitors, just does some peer_pad_caps */ + GST_VALIDATE_MONITOR_LOCK (pad_monitor); + + gst_query_parse_caps (query, &filter); + gst_query_parse_caps_result (query, &res); + if (GST_PAD_DIRECTION (pad) == GST_PAD_SINK) { + gst_validate_pad_monitor_check_caps_fields_proxied (pad_monitor, res, + filter); + } + GST_VALIDATE_MONITOR_UNLOCK (pad_monitor); + break; + } + default: + break; + } + } + + return ret; +} + +static gboolean +gst_validate_pad_monitor_activatemode_func (GstPad * pad, GstObject * parent, + GstPadMode mode, gboolean active) +{ + GstValidatePadMonitor *pad_monitor = + g_object_get_data ((GObject *) pad, "validate-monitor"); + gboolean ret = TRUE; + + /* TODO add overrides for activate func */ + + if (pad_monitor->activatemode_func) + ret = pad_monitor->activatemode_func (pad, parent, mode, active); + if (ret && active == FALSE) { + GST_VALIDATE_MONITOR_LOCK (pad_monitor); + gst_validate_pad_monitor_flush (pad_monitor); + GST_VALIDATE_MONITOR_UNLOCK (pad_monitor); + } + + return ret; +} + +static gboolean +gst_validate_pad_get_range_func (GstPad * pad, GstObject * parent, + guint64 offset, guint size, GstBuffer ** buffer) +{ + GstValidatePadMonitor *pad_monitor = + g_object_get_data ((GObject *) pad, "validate-monitor"); + gboolean ret; + ret = pad_monitor->getrange_func (pad, parent, offset, size, buffer); + return ret; +} + +static gboolean +gst_validate_pad_monitor_buffer_probe (GstPad * pad, GstBuffer * buffer, + gpointer udata) +{ + GstValidatePadMonitor *monitor = udata; + + GST_VALIDATE_PAD_MONITOR_PARENT_LOCK (monitor); + GST_VALIDATE_MONITOR_LOCK (monitor); + + gst_validate_pad_monitor_check_first_buffer (monitor, buffer); + gst_validate_pad_monitor_update_buffer_data (monitor, buffer); + gst_validate_pad_monitor_check_eos (monitor, buffer); + + if (PAD_PARENT_IS_DECODER (monitor) || PAD_PARENT_IS_ENCODER (monitor)) { + GstClockTime tolerance = 0; + + if (monitor->caps_is_audio) + tolerance = AUDIO_TIMESTAMP_TOLERANCE; + + gst_validate_pad_monitor_check_buffer_timestamp_in_received_range (monitor, + buffer, tolerance); + } + + gst_validate_pad_monitor_check_late_serialized_events (monitor, + GST_BUFFER_TIMESTAMP (buffer)); + + /* a GstValidatePadMonitor parent must be a GstValidateElementMonitor */ + if (PAD_PARENT_IS_DECODER (monitor)) { + + /* should not push out of segment data */ + if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (buffer)) && + GST_CLOCK_TIME_IS_VALID (GST_BUFFER_DURATION (buffer)) && + ((!gst_segment_clip (&monitor->segment, monitor->segment.format, + GST_BUFFER_TIMESTAMP (buffer), + GST_BUFFER_TIMESTAMP (buffer) + + GST_BUFFER_DURATION (buffer), NULL, NULL)) || + /* In the case of raw data, buffers should be strictly contained inside the + * segment */ + (monitor->caps_is_raw && + GST_BUFFER_PTS (buffer) + GST_BUFFER_DURATION (buffer) < + monitor->segment.start)) + ) { + /* TODO is this a timestamp issue? */ + GST_VALIDATE_REPORT (monitor, BUFFER_IS_OUT_OF_SEGMENT, + "buffer is out of segment and shouldn't be pushed. Timestamp: %" + GST_TIME_FORMAT " - duration: %" GST_TIME_FORMAT ". Range: %" + GST_TIME_FORMAT " - %" GST_TIME_FORMAT, + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)), + GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)), + GST_TIME_ARGS (monitor->segment.start), + GST_TIME_ARGS (monitor->segment.stop)); + } + } + + GST_VALIDATE_MONITOR_UNLOCK (monitor); + GST_VALIDATE_PAD_MONITOR_PARENT_UNLOCK (monitor); + gst_validate_pad_monitor_buffer_probe_overrides (monitor, buffer); + return TRUE; +} + +static gboolean +gst_validate_pad_monitor_event_probe (GstPad * pad, GstEvent * event, + gpointer udata) +{ + GstValidatePadMonitor *monitor = GST_VALIDATE_PAD_MONITOR_CAST (udata); + gboolean ret; + + GST_VALIDATE_PAD_MONITOR_PARENT_LOCK (monitor); + GST_VALIDATE_MONITOR_LOCK (monitor); + + GST_DEBUG_OBJECT (pad, "event %p %s", event, GST_EVENT_TYPE_NAME (event)); + + if (GST_EVENT_IS_SERIALIZED (event)) { + gint i; + + /* Detect if events the element received are being forwarded in the same order + * + * Several scenarios: + * 1) The element pushes the event as-is + * 2) The element consumes the event and does not forward it + * 3) The element consumes the event and creates another one instead + * 4) The element pushes other serialized event before pushing out the + * one it received + * + * For each pad we have two lists to track serialized events: + * 1) We received on input and expect to see (serialized_events) + * 2) We received on input but don't expect to see (expired_events) + * + * To detect events that are pushed in a different order from the one they were + * received in we check that: + * + * For each event being outputted: + * If it is in the expired_events list: + * RAISE WARNING + * If it is in the serialized_events list: + * If there are other events that were received before: + * Put those events on the expired_events list + * Remove that event and any previous ones from the serialized_events list + * + * Clear expired events list when flushing or on pad deactivation + * + */ + + if (g_list_find (monitor->expired_events, event)) { + gchar *event_str = _get_event_string (event); + /* If it's the expired events, we've failed */ + GST_WARNING_OBJECT (pad, "Did not expect event %p %s", event, + GST_EVENT_TYPE_NAME (event)); + GST_VALIDATE_REPORT (monitor, EVENT_SERIALIZED_OUT_OF_ORDER, + "Serialized event was pushed out of order: %s", event_str); + + g_free (event_str); + monitor->expired_events = g_list_remove (monitor->expired_events, event); + gst_event_unref (event); /* remove the ref that was on the list */ + } else if (monitor->serialized_events->len) { + for (i = 0; i < monitor->serialized_events->len; i++) { + SerializedEventData *next_event = + g_ptr_array_index (monitor->serialized_events, i); + GST_DEBUG_OBJECT (pad, "Checking against stored event #%d: %p %s", i, + next_event->event, GST_EVENT_TYPE_NAME (next_event->event)); + + if (event == next_event->event + || GST_EVENT_TYPE (event) == GST_EVENT_TYPE (next_event->event)) { + /* We have found our event */ + GST_DEBUG_OBJECT (pad, "Found matching event"); + + while (monitor->serialized_events->len > i + && GST_EVENT_TYPE (event) == GST_EVENT_TYPE (next_event->event)) { + /* Swallow all expected events of the same type */ + g_ptr_array_remove_index (monitor->serialized_events, i); + next_event = g_ptr_array_index (monitor->serialized_events, i); + } + + /* Move all previous events to expired events */ + if (G_UNLIKELY (i > 0)) { + GST_DEBUG_OBJECT (pad, + "Moving previous expected events to expired list"); + while (i--) { + next_event = g_ptr_array_index (monitor->serialized_events, 0); + monitor->expired_events = + g_list_append (monitor->expired_events, + gst_event_ref (next_event->event)); + g_ptr_array_remove_index (monitor->serialized_events, 0); + } + } + debug_pending_event (pad, monitor->serialized_events); + break; + } + } + } + } + + /* This so far is just like an event that is flowing downstream, + * so we do the same checks as a sinkpad event handler */ + ret = + gst_validate_pad_monitor_downstream_event_check (monitor, NULL, event, + NULL); + GST_VALIDATE_MONITOR_UNLOCK (monitor); + GST_VALIDATE_PAD_MONITOR_PARENT_UNLOCK (monitor); + + return ret; +} + +static GstPadProbeReturn +gst_validate_pad_monitor_pad_probe (GstPad * pad, GstPadProbeInfo * info, + gpointer udata) +{ + if (info->type & GST_PAD_PROBE_TYPE_BUFFER) + gst_validate_pad_monitor_buffer_probe (pad, info->data, udata); + else if (info->type & GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM) + gst_validate_pad_monitor_event_probe (pad, info->data, udata); + + return GST_PAD_PROBE_OK; +} + +static void +gst_validate_pad_monitor_update_caps_info (GstValidatePadMonitor * pad_monitor, + GstCaps * caps) +{ + GstStructure *structure; + + g_return_if_fail (gst_caps_is_fixed (caps)); + + pad_monitor->caps_is_audio = FALSE; + pad_monitor->caps_is_video = FALSE; + + structure = gst_caps_get_structure (caps, 0); + if (g_str_has_prefix (gst_structure_get_name (structure), "audio/")) { + pad_monitor->caps_is_audio = TRUE; + } else if (g_str_has_prefix (gst_structure_get_name (structure), "video/")) { + pad_monitor->caps_is_video = TRUE; + } + + if (g_str_has_prefix (gst_structure_get_name (structure), "audio/x-raw") || + g_str_has_prefix (gst_structure_get_name (structure), "video/x-raw")) { + pad_monitor->caps_is_raw = TRUE; + } else { + pad_monitor->caps_is_raw = FALSE; + } +} + +static void +gst_validate_pad_monitor_setcaps_pre (GstValidatePadMonitor * pad_monitor, + GstCaps * caps) +{ + GstStructure *structure; + + /* Check if caps are identical to last caps and complain if so + * Only checked for sink pads as src pads might push the same caps + * multiple times during unlinked/autoplugging scenarios */ + if (GST_PAD_IS_SINK (GST_VALIDATE_PAD_MONITOR_GET_PAD (pad_monitor)) && + pad_monitor->last_caps + && gst_caps_is_equal (caps, pad_monitor->last_caps)) { + gchar *caps_str = gst_caps_to_string (caps); + + GST_VALIDATE_REPORT (pad_monitor, EVENT_CAPS_DUPLICATE, "%s", caps_str); + g_free (caps_str); + + } + + gst_validate_pad_monitor_check_caps_complete (pad_monitor, caps); + + if (caps) { + structure = gst_caps_get_structure (caps, 0); + if (gst_structure_n_fields (pad_monitor->pending_setcaps_fields)) { + gint i; + for (i = 0; + i < gst_structure_n_fields (pad_monitor->pending_setcaps_fields); + i++) { + const gchar *name = + gst_structure_nth_field_name (pad_monitor->pending_setcaps_fields, + i); + const GValue *v = gst_structure_get_value (structure, name); + const GValue *otherv = + gst_structure_get_value (pad_monitor->pending_setcaps_fields, name); + + if (v == NULL) { + gchar *caps_str = gst_caps_to_string (caps); + + GST_VALIDATE_REPORT (pad_monitor, CAPS_EXPECTED_FIELD_NOT_FOUND, + "Field %s is missing from setcaps caps '%s'", name, caps_str); + g_free (caps_str); + } else if (gst_value_compare (v, otherv) != GST_VALUE_EQUAL) { + gchar *caps_str = gst_caps_to_string (caps), + *pending_setcaps_fields_str = + gst_structure_to_string (pad_monitor->pending_setcaps_fields); + + + GST_VALIDATE_REPORT (pad_monitor, CAPS_FIELD_UNEXPECTED_VALUE, + "Field %s from setcaps caps '%s' is different " + "from expected value in caps '%s'", name, caps_str, + pending_setcaps_fields_str); + + g_free (pending_setcaps_fields_str); + g_free (caps_str); + } + } + } + + if (GST_PAD_IS_SINK (GST_VALIDATE_PAD_MONITOR_GET_PAD (pad_monitor)) && + gst_validate_pad_monitor_pad_should_proxy_othercaps (pad_monitor)) { + if (_structure_is_video (structure)) { + GST_DEBUG_OBJECT (GST_VALIDATE_PAD_MONITOR_GET_PAD (pad_monitor), + "Adding video common pending fields to other pad: %" GST_PTR_FORMAT, + structure); + gst_validate_pad_monitor_otherpad_add_pending_field (pad_monitor, + structure, "width"); + gst_validate_pad_monitor_otherpad_add_pending_field (pad_monitor, + structure, "height"); + gst_validate_pad_monitor_otherpad_add_pending_field (pad_monitor, + structure, "framerate"); + gst_validate_pad_monitor_otherpad_add_pending_field (pad_monitor, + structure, "pixel-aspect-ratio"); + } else if (_structure_is_audio (structure)) { + GST_DEBUG_OBJECT (GST_VALIDATE_PAD_MONITOR_GET_PAD (pad_monitor), + "Adding audio common pending fields to other pad: %" GST_PTR_FORMAT, + structure); + gst_validate_pad_monitor_otherpad_add_pending_field (pad_monitor, + structure, "rate"); + gst_validate_pad_monitor_otherpad_add_pending_field (pad_monitor, + structure, "channels"); + } + } + } + + gst_structure_free (pad_monitor->pending_setcaps_fields); + pad_monitor->pending_setcaps_fields = + gst_structure_new_empty (PENDING_FIELDS); + + gst_validate_pad_monitor_setcaps_overrides (pad_monitor, caps); +} + +static void +gst_validate_pad_monitor_setcaps_post (GstValidatePadMonitor * pad_monitor, + GstCaps * caps, gboolean ret) +{ + if (!ret) + gst_validate_pad_monitor_otherpad_clear_pending_fields (pad_monitor); + else { + if (pad_monitor->last_caps) { + gst_caps_unref (pad_monitor->last_caps); + } + pad_monitor->last_caps = gst_caps_ref (caps); + gst_validate_pad_monitor_update_caps_info (pad_monitor, caps); + } +} + +static gboolean +gst_validate_pad_monitor_do_setup (GstValidateMonitor * monitor) +{ + GstValidatePadMonitor *pad_monitor = GST_VALIDATE_PAD_MONITOR_CAST (monitor); + GstPad *pad; + if (!GST_IS_PAD (GST_VALIDATE_MONITOR_GET_OBJECT (monitor))) { + GST_WARNING_OBJECT (monitor, "Trying to create pad monitor with other " + "type of object"); + return FALSE; + } + + pad = GST_VALIDATE_PAD_MONITOR_GET_PAD (pad_monitor); + + if (g_object_get_data ((GObject *) pad, "validate-monitor")) { + GST_WARNING_OBJECT (pad_monitor, + "Pad already has a validate-monitor associated"); + return FALSE; + } + + g_object_set_data ((GObject *) pad, "validate-monitor", pad_monitor); + + pad_monitor->pad = pad; + + pad_monitor->event_func = GST_PAD_EVENTFUNC (pad); + pad_monitor->query_func = GST_PAD_QUERYFUNC (pad); + pad_monitor->activatemode_func = GST_PAD_ACTIVATEMODEFUNC (pad); + if (GST_PAD_DIRECTION (pad) == GST_PAD_SINK) { + + pad_monitor->chain_func = GST_PAD_CHAINFUNC (pad); + if (pad_monitor->chain_func) + gst_pad_set_chain_function (pad, gst_validate_pad_monitor_chain_func); + + gst_pad_set_event_function (pad, gst_validate_pad_monitor_sink_event_func); + } else { + pad_monitor->getrange_func = GST_PAD_GETRANGEFUNC (pad); + if (pad_monitor->getrange_func) + gst_pad_set_getrange_function (pad, gst_validate_pad_get_range_func); + + gst_pad_set_event_function (pad, gst_validate_pad_monitor_src_event_func); + + /* add buffer/event probes */ + pad_monitor->pad_probe_id = + gst_pad_add_probe (pad, + GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM | + GST_PAD_PROBE_TYPE_EVENT_FLUSH, + (GstPadProbeCallback) gst_validate_pad_monitor_pad_probe, pad_monitor, + NULL); + } + gst_pad_set_query_function (pad, gst_validate_pad_monitor_query_func); + gst_pad_set_activatemode_function (pad, + gst_validate_pad_monitor_activatemode_func); + + gst_validate_reporter_set_name (GST_VALIDATE_REPORTER (monitor), + g_strdup_printf ("%s:%s", GST_DEBUG_PAD_NAME (pad))); + + if (G_UNLIKELY (GST_PAD_PARENT (pad) == NULL)) + GST_FIXME ("Saw a pad not belonging to any object"); + + return TRUE; +} diff --git a/validate/gst/validate/gst-validate-pad-monitor.h b/validate/gst/validate/gst-validate-pad-monitor.h new file mode 100644 index 0000000..82737be --- /dev/null +++ b/validate/gst/validate/gst-validate-pad-monitor.h @@ -0,0 +1,140 @@ +/* GStreamer + * Copyright (C) 2013 Thiago Santos + * + * gst-validate-pad-monitor.h - Validate PadMonitor class + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_VALIDATE_PAD_MONITOR_H__ +#define __GST_VALIDATE_PAD_MONITOR_H__ + +#include +#include + +typedef struct _GstValidatePadMonitor GstValidatePadMonitor; +typedef struct _GstValidatePadMonitorClass GstValidatePadMonitorClass; + +#include +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_VALIDATE_PAD_MONITOR (gst_validate_pad_monitor_get_type ()) +#define GST_IS_VALIDATE_PAD_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_VALIDATE_PAD_MONITOR)) +#define GST_IS_VALIDATE_PAD_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_VALIDATE_PAD_MONITOR)) +#define GST_VALIDATE_PAD_MONITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_VALIDATE_PAD_MONITOR, GstValidatePadMonitorClass)) +#define GST_VALIDATE_PAD_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_VALIDATE_PAD_MONITOR, GstValidatePadMonitor)) +#define GST_VALIDATE_PAD_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_VALIDATE_PAD_MONITOR, GstValidatePadMonitorClass)) +#define GST_VALIDATE_PAD_MONITOR_CAST(obj) ((GstValidatePadMonitor*)(obj)) +#define GST_VALIDATE_PAD_MONITOR_CLASS_CAST(klass) ((GstValidatePadMonitorClass*)(klass)) + +#define GST_VALIDATE_PAD_MONITOR_GET_PAD(m) (GST_PAD_CAST (GST_VALIDATE_MONITOR_GET_OBJECT (m))) + + +/** + * GstValidatePadMonitor: + * + * GStreamer Validate PadMonitor class. + * + * Class that wraps a #GstPad for Validate checks + */ +struct _GstValidatePadMonitor { + GstValidateMonitor parent; + + GstValidateElementMonitor *element_monitor; + + gboolean setup; + GstPad *pad; + + GstPadChainFunction chain_func; + GstPadEventFunction event_func; + GstPadGetRangeFunction getrange_func; + GstPadQueryFunction query_func; + GstPadActivateModeFunction activatemode_func; + + gulong pad_probe_id; + + /*< private >*/ + /* Last caps pushed/received */ + GstCaps *last_caps; + gboolean caps_is_audio; + gboolean caps_is_video; + gboolean caps_is_raw; + + /* FIXME : Let's migrate all those booleans into a 32 (or 64) bit flag */ + gboolean first_buffer; + + gboolean has_segment; + gboolean is_eos; + + gboolean pending_flush_stop; + guint32 pending_flush_stop_seqnum; + guint32 pending_flush_start_seqnum; + guint32 pending_newsegment_seqnum; + guint32 pending_eos_seqnum; + + GstEvent *expected_segment; + GPtrArray *serialized_events; + GList *expired_events; + + GstStructure *pending_setcaps_fields; + + /* tracked data */ + GstSegment segment; + GstClockTime current_timestamp; + GstClockTime current_duration; + + GstFlowReturn last_flow_return; + + /* Stores the timestamp range of data that has flown through + * this pad by using TIMESTAMP and TIMESTAMP+DURATION from + * incomming buffers. Every time a buffer is pushed, this range + * is extended. + * + * When a buffer is pushed, the timestamp range is checked against + * the outgoing timestamp to check it is in the received boundaries. + */ + GstClockTime timestamp_range_start; + GstClockTime timestamp_range_end; + + /* GstMediaCheck related fields */ + GList *all_bufs; + /* The GstBuffer that should arrive next in a GList */ + GList *current_buf; + gboolean check_buffers; +}; + +/** + * GstValidatePadMonitorClass: + * @parent_class: parent + * + * GStreamer Validate PadMonitor object class. + */ +struct _GstValidatePadMonitorClass { + GstValidateMonitorClass parent_class; +}; + +/* normal GObject stuff */ +GType gst_validate_pad_monitor_get_type (void); + +GstValidatePadMonitor * gst_validate_pad_monitor_new (GstPad * pad, GstValidateRunner * runner, GstValidateElementMonitor *element_monitor); + +G_END_DECLS + +#endif /* __GST_VALIDATE_PAD_MONITOR_H__ */ + diff --git a/validate/gst/validate/gst-validate-pipeline-monitor.c b/validate/gst/validate/gst-validate-pipeline-monitor.c new file mode 100644 index 0000000..2a2dbdf --- /dev/null +++ b/validate/gst/validate/gst-validate-pipeline-monitor.c @@ -0,0 +1,240 @@ +/* GStreamer + * + * Copyright (C) 2014 Thibault Saunier + * + * gst-validate-pipeline-monitor.c - Validate PipelineMonitor class + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "gst-validate-internal.h" +#include "gst-validate-pipeline-monitor.h" +#include "gst-validate-monitor-factory.h" + +#define PRINT_POSITION_TIMEOUT 250 + +/** + * SECTION:gst-validate-pipeline-monitor + * @short_description: Class that wraps a #GstPipeline for Validate checks + * + * TODO + */ + +enum +{ + PROP_LAST +}; + +#define gst_validate_pipeline_monitor_parent_class parent_class +G_DEFINE_TYPE (GstValidatePipelineMonitor, gst_validate_pipeline_monitor, + GST_TYPE_VALIDATE_BIN_MONITOR); + +static void +gst_validate_pipeline_monitor_class_init (GstValidatePipelineMonitorClass * + klass) +{ +} + +static void +gst_validate_pipeline_monitor_init (GstValidatePipelineMonitor * + pipeline_monitor) +{ +} + +static gboolean +print_position (GstValidateMonitor * monitor) +{ + GstQuery *query; + gint64 position, duration; + GstElement *pipeline = + GST_ELEMENT (GST_VALIDATE_MONITOR_GET_OBJECT (monitor)); + + gdouble rate = 1.0; + GstFormat format = GST_FORMAT_TIME; + + if (!gst_element_query_position (pipeline, format, &position)) { + GST_DEBUG_OBJECT (monitor, "Could not query position"); + + return TRUE; + } + + format = GST_FORMAT_TIME; + if (!gst_element_query_duration (pipeline, format, &duration)) { + GST_DEBUG_OBJECT (monitor, "Could not query duration"); + + return TRUE; + } + + query = gst_query_new_segment (GST_FORMAT_DEFAULT); + if (gst_element_query (pipeline, query)) + gst_query_parse_segment (query, &rate, NULL, NULL, NULL); + gst_query_unref (query); + + gst_validate_printf (NULL, + "\r", GST_TIME_ARGS (position), GST_TIME_ARGS (duration), + rate); + + return TRUE; +} + +static void +_bus_handler (GstBus * bus, GstMessage * message, + GstValidatePipelineMonitor * monitor) +{ + GError *err = NULL; + gchar *debug = NULL; + + switch (GST_MESSAGE_TYPE (message)) { + case GST_MESSAGE_ERROR: + gst_message_parse_error (message, &err, &debug); + + if (g_error_matches (err, GST_CORE_ERROR, GST_CORE_ERROR_MISSING_PLUGIN)) { + GST_VALIDATE_REPORT (monitor, MISSING_PLUGIN, + "Error: %s -- Debug message: %s", err->message, debug); + } else { + GST_VALIDATE_REPORT (monitor, ERROR_ON_BUS, + "Got error: %s -- Debug message: %s", err->message, debug); + } + GST_VALIDATE_MONITOR_LOCK (monitor); + monitor->got_error = TRUE; + GST_VALIDATE_MONITOR_UNLOCK (monitor); + g_error_free (err); + g_free (debug); + break; + case GST_MESSAGE_WARNING: + gst_message_parse_warning (message, &err, &debug); + GST_VALIDATE_REPORT (monitor, WARNING_ON_BUS, + "Got warning: %s -- Debug message: %s", err->message, debug); + g_error_free (err); + g_free (debug); + break; + case GST_MESSAGE_STATE_CHANGED: + { + if (GST_MESSAGE_SRC (message) == GST_VALIDATE_MONITOR (monitor)->target) { + GstState oldstate, newstate, pending; + + gst_message_parse_state_changed (message, &oldstate, &newstate, + &pending); + + if (oldstate == GST_STATE_READY && newstate == GST_STATE_PAUSED) { + monitor->print_pos_srcid = + g_timeout_add (PRINT_POSITION_TIMEOUT, + (GSourceFunc) print_position, monitor); + } else if (oldstate >= GST_STATE_PAUSED && newstate <= GST_STATE_READY) { + if (monitor->print_pos_srcid + && g_source_remove (monitor->print_pos_srcid)) + monitor->print_pos_srcid = 0; + monitor->got_error = FALSE; + } + } + + break; + } + case GST_MESSAGE_BUFFERING: + { + GstBufferingMode mode; + gint percent; + + gst_message_parse_buffering (message, &percent); + gst_message_parse_buffering_stats (message, &mode, NULL, NULL, NULL); + + if (percent == 100) { + /* a 100% message means buffering is done */ + if (monitor->buffering) { + monitor->print_pos_srcid = + g_timeout_add (PRINT_POSITION_TIMEOUT, + (GSourceFunc) print_position, monitor); + monitor->buffering = FALSE; + } + } else { + /* buffering... */ + if (!monitor->buffering) { + monitor->buffering = TRUE; + if (monitor->print_pos_srcid + && g_source_remove (monitor->print_pos_srcid)) + monitor->print_pos_srcid = 0; + } + } + break; + } + default: + break; + } +} + +static void +gst_validate_pipeline_monitor_create_scenarios (GstValidateBinMonitor * monitor) +{ + /* scenarios currently only make sense for pipelines */ + const gchar *scenario_name; + + if ((scenario_name = g_getenv ("GST_VALIDATE_SCENARIO"))) { + gchar **scenario_v = g_strsplit (scenario_name, "->", 2); + + if (scenario_v[1] && GST_VALIDATE_MONITOR_GET_OBJECT (monitor)) { + if (!g_pattern_match_simple (scenario_v[1], + GST_OBJECT_NAME (GST_VALIDATE_MONITOR_GET_OBJECT (monitor)))) { + GST_INFO_OBJECT (monitor, "Not attaching to pipeline %" GST_PTR_FORMAT + " as not matching pattern %s", + GST_VALIDATE_MONITOR_GET_OBJECT (monitor), scenario_v[1]); + + g_strfreev (scenario_v); + return; + } + } + monitor->scenario = + gst_validate_scenario_factory_create (GST_VALIDATE_MONITOR_GET_RUNNER + (monitor), + GST_ELEMENT_CAST (GST_VALIDATE_MONITOR_GET_OBJECT (monitor)), + scenario_v[0]); + g_strfreev (scenario_v); + } +} + +/** + * gst_validate_pipeline_monitor_new: + * @pipeline: (transfer-none): a #GstPipeline to run Validate on + */ +GstValidatePipelineMonitor * +gst_validate_pipeline_monitor_new (GstPipeline * pipeline, + GstValidateRunner * runner, GstValidateMonitor * parent) +{ + GstBus *bus; + GstValidatePipelineMonitor *monitor = + g_object_new (GST_TYPE_VALIDATE_PIPELINE_MONITOR, "object", + pipeline, "validate-runner", runner, "validate-parent", parent, NULL); + + if (GST_VALIDATE_MONITOR_GET_OBJECT (monitor) == NULL) { + g_object_unref (monitor); + return NULL; + } + + gst_validate_pipeline_monitor_create_scenarios (GST_VALIDATE_BIN_MONITOR + (monitor)); + + bus = gst_element_get_bus (GST_ELEMENT (pipeline)); + gst_bus_enable_sync_message_emission (bus); + g_signal_connect (bus, "sync-message", (GCallback) _bus_handler, monitor); + + gst_object_unref (bus); + + return monitor; +} diff --git a/validate/gst/validate/gst-validate-pipeline-monitor.h b/validate/gst/validate/gst-validate-pipeline-monitor.h new file mode 100644 index 0000000..6e50961 --- /dev/null +++ b/validate/gst/validate/gst-validate-pipeline-monitor.h @@ -0,0 +1,82 @@ +/* GStreamer + * Copyright (C) 2014 Thibault Saunier + * + * gst-validate-pipeline-monitor.h - Validate PipelineMonitor class + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_VALIDATE_PIPELINE_MONITOR_H__ +#define __GST_VALIDATE_PIPELINE_MONITOR_H__ + +#include +#include +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_VALIDATE_PIPELINE_MONITOR (gst_validate_pipeline_monitor_get_type ()) +#define GST_IS_VALIDATE_PIPELINE_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_VALIDATE_PIPELINE_MONITOR)) +#define GST_IS_VALIDATE_PIPELINE_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_VALIDATE_PIPELINE_MONITOR)) +#define GST_VALIDATE_PIPELINE_MONITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_VALIDATE_PIPELINE_MONITOR, GstValidatePipelineMonitorClass)) +#define GST_VALIDATE_PIPELINE_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_VALIDATE_PIPELINE_MONITOR, GstValidatePipelineMonitor)) +#define GST_VALIDATE_PIPELINE_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_VALIDATE_PIPELINE_MONITOR, GstValidatePipelineMonitorClass)) +#define GST_VALIDATE_PIPELINE_MONITOR_CAST(obj) ((GstValidatePipelineMonitor*)(obj)) +#define GST_VALIDATE_PIPELINE_MONITOR_CLASS_CAST(klass) ((GstValidatePipelineMonitorClass*)(klass)) + +#define GST_VALIDATE_PIPELINE_MONITOR_GET_PIPELINE(m) (GST_PIPELINE_CAST (GST_VALIDATE_ELEMENT_MONITOR_GET_ELEMENT (m))) + +typedef struct _GstValidatePipelineMonitor GstValidatePipelineMonitor; +typedef struct _GstValidatePipelineMonitorClass GstValidatePipelineMonitorClass; + +/** + * GstValidatePipelineMonitor: + * + * GStreamer Validate PipelineMonitor class. + * + * Class that wraps a #GstPipeline for Validate checks + */ +struct _GstValidatePipelineMonitor { + GstValidateBinMonitor parent; + + /*< private >*/ + gulong element_added_id; + guint print_pos_srcid; + gboolean buffering; + gboolean got_error; +}; + +/** + * GstValidatePipelineMonitorClass: + * @parent_class: parent + * + * GStreamer Validate PipelineMonitor object class. + */ +struct _GstValidatePipelineMonitorClass { + GstValidateBinMonitorClass parent_class; +}; + +/* normal GObject stuff */ +GType gst_validate_pipeline_monitor_get_type (void); + +GstValidatePipelineMonitor * gst_validate_pipeline_monitor_new (GstPipeline * pipeline, + GstValidateRunner * runner, GstValidateMonitor * parent); + +G_END_DECLS + +#endif /* __GST_VALIDATE_PIPELINE_MONITOR_H__ */ + diff --git a/validate/gst/validate/gst-validate-report.c b/validate/gst/validate/gst-validate-report.c new file mode 100644 index 0000000..16b1bb8 --- /dev/null +++ b/validate/gst/validate/gst-validate-report.c @@ -0,0 +1,891 @@ +/* GStreamer + * + * Copyright (C) 2013 Collabora Ltd. + * Author: Thiago Sousa Santos + * + * gst-validate-monitor-report.c - Validate report/issues functions + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include /* fprintf */ +#include +#include + +#include +#include "gst-validate-i18n-lib.h" +#include "gst-validate-internal.h" + +#include "gst-validate-report.h" +#include "gst-validate-reporter.h" +#include "gst-validate-monitor.h" +#include "gst-validate-scenario.h" + +static GstClockTime _gst_validate_report_start_time = 0; +static GstValidateDebugFlags _gst_validate_flags = 0; +static GHashTable *_gst_validate_issues = NULL; +static FILE **log_files = NULL; + + +GRegex *newline_regex = NULL; + +GST_DEBUG_CATEGORY_STATIC (gst_validate_report_debug); +#undef GST_CAT_DEFAULT +#define GST_CAT_DEFAULT gst_validate_report_debug + +#define GST_VALIDATE_REPORT_SHADOW_REPORTS_LOCK(r) \ + G_STMT_START { \ + (g_mutex_lock (&((GstValidateReport *) r)->shadow_reports_lock)); \ + } G_STMT_END + +#define GST_VALIDATE_REPORT_SHADOW_REPORTS_UNLOCK(r) \ + G_STMT_START { \ + (g_mutex_unlock (&((GstValidateReport *) r)->shadow_reports_lock)); \ + } G_STMT_END + +G_DEFINE_BOXED_TYPE (GstValidateReport, gst_validate_report, + (GBoxedCopyFunc) gst_validate_report_ref, + (GBoxedFreeFunc) gst_validate_report_unref); + +static GstValidateIssue * +gst_validate_issue_ref (GstValidateIssue * issue) +{ + g_return_val_if_fail (issue != NULL, NULL); + + g_atomic_int_inc (&issue->refcount); + + return issue; +} + +static void +gst_validate_issue_unref (GstValidateIssue * issue) +{ + if (G_UNLIKELY (g_atomic_int_dec_and_test (&issue->refcount))) { + g_free (issue->summary); + g_free (issue->description); + + /* We are using an string array for area and name */ + g_strfreev (&issue->area); + + g_slice_free (GstValidateIssue, issue); + } +} + + +G_DEFINE_BOXED_TYPE (GstValidateIssue, gst_validate_issue, + (GBoxedCopyFunc) gst_validate_issue_ref, + (GBoxedFreeFunc) gst_validate_issue_unref); + +GstValidateIssueId +gst_validate_issue_get_id (GstValidateIssue * issue) +{ + return issue->issue_id; +} + +/** + * gst_validate_issue_new: + * @issue_id: The ID of the issue, should be a GQuark + * @summary: A summary of the issue + * @description: A more complete of what the issue is about + * @default_level: The level at which the issue will be reported by default + * + * Returns: (transfer full): The newly created #GstValidateIssue + */ +GstValidateIssue * +gst_validate_issue_new (GstValidateIssueId issue_id, const gchar * summary, + const gchar * description, GstValidateReportLevel default_level) +{ + GstValidateIssue *issue = g_slice_new (GstValidateIssue); + gchar **area_name = g_strsplit (g_quark_to_string (issue_id), "::", 2); + + g_return_val_if_fail (area_name[0] != NULL && area_name[1] != 0 && + area_name[2] == NULL, NULL); + + issue->issue_id = issue_id; + issue->summary = g_strdup (summary); + issue->description = g_strdup (description); + issue->default_level = default_level; + issue->area = area_name[0]; + issue->name = area_name[1]; + + g_free (area_name); + return issue; +} + +void +gst_validate_issue_set_default_level (GstValidateIssue * issue, + GstValidateReportLevel default_level) +{ + GST_INFO ("Setting issue %s::%s default level to %s", + issue->area, issue->name, + gst_validate_report_level_get_name (default_level)); + + issue->default_level = default_level; +} + +/** + * gst_validate_issue_register: + * @issue: (transfer none): The #GstValidateIssue to register + * + * Registers @issue in the issue type system + */ +void +gst_validate_issue_register (GstValidateIssue * issue) +{ + g_return_if_fail (g_hash_table_lookup (_gst_validate_issues, + (gpointer) gst_validate_issue_get_id (issue)) == NULL); + + g_hash_table_insert (_gst_validate_issues, + (gpointer) gst_validate_issue_get_id (issue), issue); +} + +#define REGISTER_VALIDATE_ISSUE(lvl,id,sum,desc) \ + gst_validate_issue_register (gst_validate_issue_new (id, \ + sum, desc, GST_VALIDATE_REPORT_LEVEL_##lvl)) +static void +gst_validate_report_load_issues (void) +{ + g_return_if_fail (_gst_validate_issues == NULL); + + _gst_validate_issues = g_hash_table_new_full (g_direct_hash, g_direct_equal, + NULL, (GDestroyNotify) gst_validate_issue_unref); + + REGISTER_VALIDATE_ISSUE (WARNING, BUFFER_BEFORE_SEGMENT, + _("buffer was received before a segment"), + _("in push mode, a segment event must be received before a buffer")); + REGISTER_VALIDATE_ISSUE (ISSUE, BUFFER_IS_OUT_OF_SEGMENT, + _("buffer is out of the segment range"), + _("buffer being pushed is out of the current segment's start-stop " + " range. Meaning it is going to be discarded downstream without " + "any use")); + REGISTER_VALIDATE_ISSUE (WARNING, BUFFER_TIMESTAMP_OUT_OF_RECEIVED_RANGE, + _("buffer timestamp is out of the received buffer timestamps' range"), + _("a buffer leaving an element should have its timestamps in the range " + "of the received buffers timestamps. i.e. If an element received " + "buffers with timestamps from 0s to 10s, it can't push a buffer with " + "with a 11s timestamp, because it doesn't have data for that")); + REGISTER_VALIDATE_ISSUE (WARNING, WRONG_BUFFER, + _("Received buffer does not correspond to wanted one."), + _("When checking playback of a file against a MediaInfo file" + " all buffers coming into the decoders might be checked" + " and should have the exact expected metadatas and hash of the" + " content")); + REGISTER_VALIDATE_ISSUE (CRITICAL, WRONG_FLOW_RETURN, + _("flow return from pad push doesn't match expected value"), + _("flow return from a 1:1 sink/src pad element is as simple as " + "returning what downstream returned. For elements that have multiple " + "src pads, flow returns should be properly combined")); + REGISTER_VALIDATE_ISSUE (ISSUE, BUFFER_AFTER_EOS, + _("buffer was received after EOS"), + _("a pad shouldn't receive any more buffers after it gets EOS")); + REGISTER_VALIDATE_ISSUE (WARNING, FLOW_ERROR_WITHOUT_ERROR_MESSAGE, + _("GST_FLOW_ERROR returned without posting an ERROR on the bus"), + _("Element MUST post a GST_MESSAGE_ERROR with GST_ELEMENT_ERROR before" + " returning GST_FLOW_ERROR")); + + REGISTER_VALIDATE_ISSUE (ISSUE, CAPS_IS_MISSING_FIELD, + _("caps is missing a required field for its type"), + _("some caps types are expected to contain a set of basic fields. " + "For example, raw video should have 'width', 'height', 'framerate' " + "and 'pixel-aspect-ratio'")); + REGISTER_VALIDATE_ISSUE (WARNING, CAPS_FIELD_HAS_BAD_TYPE, + _("caps field has an unexpected type"), + _("some common caps fields should always use the same expected types")); + REGISTER_VALIDATE_ISSUE (WARNING, CAPS_EXPECTED_FIELD_NOT_FOUND, + _("caps expected field wasn't present"), + _("a field that should be present in the caps wasn't found. " + "Fields sets on a sink pad caps should be propagated downstream " + "when it makes sense to do so")); + REGISTER_VALIDATE_ISSUE (CRITICAL, GET_CAPS_NOT_PROXYING_FIELDS, + _("getcaps function isn't proxying downstream fields correctly"), + _("elements should set downstream caps restrictions on its caps when " + "replying upstream's getcaps queries to avoid upstream sending data" + " in an unsupported format")); + REGISTER_VALIDATE_ISSUE (CRITICAL, CAPS_FIELD_UNEXPECTED_VALUE, + _("a field in caps has an unexpected value"), + _("fields set on a sink pad should be propagated downstream via " + "set caps")); + + REGISTER_VALIDATE_ISSUE (WARNING, EVENT_NEWSEGMENT_NOT_PUSHED, + _("new segment event wasn't propagated downstream"), + _("segments received from upstream should be pushed downstream")); + REGISTER_VALIDATE_ISSUE (WARNING, SERIALIZED_EVENT_WASNT_PUSHED_IN_TIME, + _("a serialized event received should be pushed in the same 'time' " + "as it was received"), + _("serialized events should be pushed in the same order they are " + "received and serialized with buffers. If an event is received after" + " a buffer with timestamp end 'X', it should be pushed right after " + "buffers with timestamp end 'X'")); + REGISTER_VALIDATE_ISSUE (ISSUE, EOS_HAS_WRONG_SEQNUM, + _("EOS events that are part of the same pipeline 'operation' should " + "have the same seqnum"), + _("when events/messages are created from another event/message, " + "they should have their seqnums set to the original event/message " + "seqnum")); + REGISTER_VALIDATE_ISSUE (ISSUE, FLUSH_START_HAS_WRONG_SEQNUM, + _ + ("FLUSH_START events that are part of the same pipeline 'operation' should " + "have the same seqnum"), + _("when events/messages are created from another event/message, " + "they should have their seqnums set to the original event/message " + "seqnum")); + REGISTER_VALIDATE_ISSUE (ISSUE, FLUSH_STOP_HAS_WRONG_SEQNUM, + _ + ("FLUSH_STOP events that are part of the same pipeline 'operation' should " + "have the same seqnum"), + _("when events/messages are created from another event/message, " + "they should have their seqnums set to the original event/message " + "seqnum")); + REGISTER_VALIDATE_ISSUE (ISSUE, SEGMENT_HAS_WRONG_SEQNUM, + _("SEGMENT events that are part of the same pipeline 'operation' should " + "have the same seqnum"), + _("when events/messages are created from another event/message, " + "they should have their seqnums set to the original event/message " + "seqnum")); + REGISTER_VALIDATE_ISSUE (WARNING, EVENT_SERIALIZED_OUT_OF_ORDER, + _("a serialized event received should be pushed in the same order " + "as it was received"), + _("serialized events should be pushed in the same order they are " + "received.")); + REGISTER_VALIDATE_ISSUE (WARNING, EVENT_NEW_SEGMENT_MISMATCH, + _("a new segment event has different value than the received one"), + _("when receiving a new segment, an element should push an equivalent" + "segment downstream")); + REGISTER_VALIDATE_ISSUE (WARNING, EVENT_FLUSH_START_UNEXPECTED, + _("received an unexpected flush start event"), NULL); + REGISTER_VALIDATE_ISSUE (WARNING, EVENT_FLUSH_STOP_UNEXPECTED, + _("received an unexpected flush stop event"), NULL); + REGISTER_VALIDATE_ISSUE (WARNING, EVENT_CAPS_DUPLICATE, + _("received the same caps twice"), NULL); + + REGISTER_VALIDATE_ISSUE (CRITICAL, EVENT_SEEK_NOT_HANDLED, + _("seek event wasn't handled"), NULL); + REGISTER_VALIDATE_ISSUE (CRITICAL, EVENT_SEEK_RESULT_POSITION_WRONG, + _("position after a seek is wrong"), NULL); + + REGISTER_VALIDATE_ISSUE (WARNING, EVENT_EOS_WITHOUT_SEGMENT, + _("EOS received without segment event before"), + _("A segment event should always be sent before data flow" + " EOS being some kind of data flow, there is no exception" + " in that regard")); + + REGISTER_VALIDATE_ISSUE (CRITICAL, STATE_CHANGE_FAILURE, + _("state change failed"), NULL); + + REGISTER_VALIDATE_ISSUE (WARNING, FILE_SIZE_INCORRECT, + _("resulting file size wasn't within the expected values"), NULL); + REGISTER_VALIDATE_ISSUE (WARNING, FILE_DURATION_INCORRECT, + _("resulting file duration wasn't within the expected values"), NULL); + REGISTER_VALIDATE_ISSUE (WARNING, FILE_SEEKABLE_INCORRECT, + _("resulting file wasn't seekable or not seekable as expected"), NULL); + REGISTER_VALIDATE_ISSUE (CRITICAL, FILE_PROFILE_INCORRECT, + _("resulting file stream profiles didn't match expected values"), NULL); + REGISTER_VALIDATE_ISSUE (ISSUE, FILE_TAG_DETECTION_INCORRECT, + _("detected tags are different than expected ones"), NULL); + REGISTER_VALIDATE_ISSUE (WARNING, FILE_NO_STREAM_ID, + _("the discoverer found a stream that had no stream ID"), NULL); + + + REGISTER_VALIDATE_ISSUE (CRITICAL, ALLOCATION_FAILURE, + _("a memory allocation failed during Validate run"), NULL); + REGISTER_VALIDATE_ISSUE (CRITICAL, MISSING_PLUGIN, + _("a gstreamer plugin is missing and prevented Validate from running"), + NULL); + REGISTER_VALIDATE_ISSUE (WARNING, WARNING_ON_BUS, + _("We got a WARNING message on the bus"), NULL); + REGISTER_VALIDATE_ISSUE (CRITICAL, ERROR_ON_BUS, + _("We got an ERROR message on the bus"), NULL); + REGISTER_VALIDATE_ISSUE (WARNING, QUERY_POSITION_SUPERIOR_DURATION, + _("Query position reported a value superior than what query duration " + "returned"), NULL); + REGISTER_VALIDATE_ISSUE (WARNING, QUERY_POSITION_OUT_OF_SEGMENT, + _("Query position reported a value outside of the current expected " + "segment"), NULL); + REGISTER_VALIDATE_ISSUE (CRITICAL, SCENARIO_NOT_ENDED, + _("All the actions were not executed before the program stopped"), NULL); + REGISTER_VALIDATE_ISSUE (CRITICAL, SCENARIO_FILE_MALFORMED, + _("The scenario file was malformed"), NULL); + REGISTER_VALIDATE_ISSUE (CRITICAL, SCENARIO_ACTION_EXECUTION_ERROR, + _("The execution of an action did not properly happen"), NULL); + REGISTER_VALIDATE_ISSUE (ISSUE, SCENARIO_ACTION_EXECUTION_ISSUE, + _("An issue happend during the execution of a scenario"), NULL); + REGISTER_VALIDATE_ISSUE (WARNING, G_LOG_WARNING, _("We got a g_log warning"), + NULL); + REGISTER_VALIDATE_ISSUE (CRITICAL, G_LOG_CRITICAL, + _("We got a g_log critical issue"), NULL); + REGISTER_VALIDATE_ISSUE (ISSUE, G_LOG_ISSUE, _("We got a g_log issue"), NULL); +} + +void +gst_validate_report_init (void) +{ + const gchar *var, *file_env; + const GDebugKey keys[] = { + {"fatal_criticals", GST_VALIDATE_FATAL_CRITICALS}, + {"fatal_warnings", GST_VALIDATE_FATAL_WARNINGS}, + {"fatal_issues", GST_VALIDATE_FATAL_ISSUES}, + {"print_issues", GST_VALIDATE_PRINT_ISSUES}, + {"print_warnings", GST_VALIDATE_PRINT_WARNINGS}, + {"print_criticals", GST_VALIDATE_PRINT_CRITICALS} + }; + + GST_DEBUG_CATEGORY_INIT (gst_validate_report_debug, "gstvalidatereport", + GST_DEBUG_FG_YELLOW, "Gst validate reporting"); + + if (_gst_validate_report_start_time == 0) { + _gst_validate_report_start_time = gst_util_get_timestamp (); + + /* init the debug flags */ + var = g_getenv ("GST_VALIDATE"); + if (var && strlen (var) > 0) { + _gst_validate_flags = + g_parse_debug_string (var, keys, G_N_ELEMENTS (keys)); + } + + gst_validate_report_load_issues (); + } + + file_env = g_getenv ("GST_VALIDATE_FILE"); + if (file_env != NULL && *file_env != '\0') { + gint i; + gchar **wanted_files; + wanted_files = g_strsplit (file_env, G_SEARCHPATH_SEPARATOR_S, 0); + + /* FIXME: Make sure it is freed in the deinit function when that is + * implemented */ + log_files = + g_malloc0 (sizeof (FILE *) * (g_strv_length (wanted_files) + 1)); + for (i = 0; i < g_strv_length (wanted_files); i++) { + FILE *log_file; + + if (g_strcmp0 (wanted_files[i], "stderr") == 0) { + log_file = stderr; + } else if (g_strcmp0 (wanted_files[i], "stdout") == 0) + log_file = stdout; + else { + log_file = g_fopen (wanted_files[i], "w"); + } + + if (log_file == NULL) { + g_printerr ("Could not open log file '%s' for writing: %s\n", file_env, + g_strerror (errno)); + log_file = stderr; + } + + log_files[i] = log_file; + } + + g_strfreev (wanted_files); + } else { + log_files = g_malloc0 (sizeof (FILE *) * 2); + log_files[0] = stdout; + } + +#ifndef GST_DISABLE_GST_DEBUG + if (!newline_regex) + newline_regex = + g_regex_new ("\n", G_REGEX_OPTIMIZE | G_REGEX_MULTILINE, 0, NULL); +#endif +} + +GstValidateIssue * +gst_validate_issue_from_id (GstValidateIssueId issue_id) +{ + return g_hash_table_lookup (_gst_validate_issues, (gpointer) issue_id); +} + +/* TODO how are these functions going to work with extensions */ +const gchar * +gst_validate_report_level_get_name (GstValidateReportLevel level) +{ + switch (level) { + case GST_VALIDATE_REPORT_LEVEL_CRITICAL: + return "critical"; + case GST_VALIDATE_REPORT_LEVEL_WARNING: + return "warning"; + case GST_VALIDATE_REPORT_LEVEL_ISSUE: + return "issue"; + case GST_VALIDATE_REPORT_LEVEL_IGNORE: + return "ignore"; + default: + return "unknown"; + } +} + +GstValidateReportLevel +gst_validate_report_level_from_name (const gchar * issue_name) +{ + if (g_strcmp0 (issue_name, "critical") == 0) + return GST_VALIDATE_REPORT_LEVEL_CRITICAL; + + else if (g_strcmp0 (issue_name, "warning") == 0) + return GST_VALIDATE_REPORT_LEVEL_WARNING; + + else if (g_strcmp0 (issue_name, "issue") == 0) + return GST_VALIDATE_REPORT_LEVEL_ISSUE; + + else if (g_strcmp0 (issue_name, "ignore") == 0) + return GST_VALIDATE_REPORT_LEVEL_IGNORE; + + return GST_VALIDATE_REPORT_LEVEL_UNKNOWN; +} + +gboolean +gst_validate_report_should_print (GstValidateReport * report) +{ + if ((!(_gst_validate_flags & GST_VALIDATE_PRINT_ISSUES) && + !(_gst_validate_flags & GST_VALIDATE_PRINT_WARNINGS) && + !(_gst_validate_flags & GST_VALIDATE_PRINT_CRITICALS))) { + return TRUE; + } + + if ((report->level <= GST_VALIDATE_REPORT_LEVEL_ISSUE && + _gst_validate_flags & GST_VALIDATE_PRINT_ISSUES) || + (report->level <= GST_VALIDATE_REPORT_LEVEL_WARNING && + _gst_validate_flags & GST_VALIDATE_PRINT_WARNINGS) || + (report->level <= GST_VALIDATE_REPORT_LEVEL_CRITICAL && + _gst_validate_flags & GST_VALIDATE_PRINT_CRITICALS)) { + + return TRUE; + } + + return FALSE; +} + +gboolean +gst_validate_report_check_abort (GstValidateReport * report) +{ + if ((report->level <= GST_VALIDATE_REPORT_LEVEL_ISSUE && + _gst_validate_flags & GST_VALIDATE_FATAL_ISSUES) || + (report->level <= GST_VALIDATE_REPORT_LEVEL_WARNING && + _gst_validate_flags & GST_VALIDATE_FATAL_WARNINGS) || + (report->level <= GST_VALIDATE_REPORT_LEVEL_CRITICAL && + _gst_validate_flags & GST_VALIDATE_FATAL_CRITICALS)) { + + return TRUE; + } + + return FALSE; +} + +GstValidateIssueId +gst_validate_report_get_issue_id (GstValidateReport * report) +{ + return gst_validate_issue_get_id (report->issue); +} + +GstValidateReport * +gst_validate_report_new (GstValidateIssue * issue, + GstValidateReporter * reporter, const gchar * message) +{ + GstValidateReport *report = g_slice_new0 (GstValidateReport); + + report->refcount = 1; + report->issue = issue; + report->reporter = reporter; /* TODO should we ref? */ + report->message = g_strdup (message); + g_mutex_init (&report->shadow_reports_lock); + report->timestamp = + gst_util_get_timestamp () - _gst_validate_report_start_time; + report->level = issue->default_level; + report->reporting_level = GST_VALIDATE_SHOW_UNKNOWN; + + return report; +} + +void +gst_validate_report_unref (GstValidateReport * report) +{ + g_return_if_fail (report != NULL); + + if (G_UNLIKELY (g_atomic_int_dec_and_test (&report->refcount))) { + g_free (report->message); + g_list_free_full (report->shadow_reports, + (GDestroyNotify) gst_validate_report_unref); + g_list_free_full (report->repeated_reports, + (GDestroyNotify) gst_validate_report_unref); + g_mutex_clear (&report->shadow_reports_lock); + g_slice_free (GstValidateReport, report); + } +} + +GstValidateReport * +gst_validate_report_ref (GstValidateReport * report) +{ + g_return_val_if_fail (report != NULL, NULL); + + g_atomic_int_inc (&report->refcount); + + return report; +} + +void +gst_validate_printf (gpointer source, const gchar * format, ...) +{ + va_list var_args; + + va_start (var_args, format); + gst_validate_printf_valist (source, format, var_args); + va_end (var_args); +} + +static gboolean +_append_value (GQuark field_id, const GValue * value, GString * string) +{ + gchar *val_str = NULL; + + if (g_strcmp0 (g_quark_to_string (field_id), "sub-action") == 0) + return TRUE; + + if (G_VALUE_TYPE (value) == GST_TYPE_CLOCK_TIME) + val_str = g_strdup_printf ("%" GST_TIME_FORMAT, + GST_TIME_ARGS (g_value_get_uint64 (value))); + else + val_str = gst_value_serialize (value); + + g_string_append (string, g_quark_to_string (field_id)); + g_string_append_len (string, "=", 1); + g_string_append (string, val_str); + g_string_append_len (string, " ", 1); + + g_free (val_str); + + return TRUE; +} + +/** + * gst_validate_print_action: + * @action: (allow-none): The source object to log + * @message: The message to print out in the GstValidate logging system + * + * Print @message to the GstValidate logging system + */ +void +gst_validate_print_action (GstValidateAction * action, const gchar * message) +{ + GString *string = NULL; + + if (message == NULL) { + gint nrepeats; + + string = g_string_new (NULL); + + if (gst_validate_action_is_subaction (action)) + g_string_append_printf (string, "(subaction)"); + + if (gst_structure_get_int (action->structure, "repeat", &nrepeats)) + g_string_append_printf (string, "(%d/%d)", action->repeat, nrepeats); + + g_string_append_printf (string, " %s", + gst_structure_get_name (action->structure)); + + g_string_append_len (string, ": ", 2); + gst_structure_foreach (action->structure, + (GstStructureForeachFunc) _append_value, string); + g_string_append_len (string, "\n", 1); + message = string->str; + } + + gst_validate_printf (action, "%s", message); + + if (string) + g_string_free (string, TRUE); +} + +static void +print_action_parametter (GString * string, GstValidateActionType * type, + GstValidateActionParameter * param) +{ + gint nw = 0; + + gchar *desc, *tmp; + gchar *param_head = g_strdup_printf (" %s", param->name); + gchar *tmp_head = g_strdup_printf ("\n %-30s : %s", + param_head, "something"); + + + while (tmp_head[nw] != ':') + nw++; + + g_free (tmp_head); + + tmp = g_strdup_printf ("\n%*s", nw + 1, " "); + + if (g_strcmp0 (param->description, "")) { + desc = + g_regex_replace (newline_regex, param->description, + -1, 0, tmp, 0, NULL); + } else { + desc = g_strdup_printf ("No description"); + } + + g_string_append_printf (string, "\n %-30s : %s", param_head, desc); + g_free (desc); + + if (param->possible_variables) { + gchar *tmp1 = g_strdup_printf ("\n%*s", nw + 4, " "); + desc = + g_regex_replace (newline_regex, + param->possible_variables, -1, 0, tmp1, 0, NULL); + g_string_append_printf (string, "%sPossible variables:%s%s", tmp, + tmp1, desc); + + g_free (tmp1); + } + + if (param->types) { + gchar *tmp1 = g_strdup_printf ("\n%*s", nw + 4, " "); + desc = g_regex_replace (newline_regex, param->types, -1, 0, tmp1, 0, NULL); + g_string_append_printf (string, "%sPossible types:%s%s", tmp, tmp1, desc); + + g_free (tmp1); + } + + if (!param->mandatory) { + g_string_append_printf (string, "%sDefault: %s", tmp, param->def); + } + + g_string_append_printf (string, "%s%s", tmp, + param->mandatory ? "Mandatory." : "Optional."); + + g_free (tmp); + g_free (param_head); +} + +void +gst_validate_printf_valist (gpointer source, const gchar * format, va_list args) +{ + gint i; + GString *string = g_string_new (NULL); + + if (source) { + if (*(GType *) source == GST_TYPE_VALIDATE_ACTION) { + GstValidateAction *action = (GstValidateAction *) source; + + if (_action_check_and_set_printed (action)) + goto out; + + g_string_printf (string, "Executing "); + + } else if (*(GType *) source == GST_TYPE_VALIDATE_ACTION_TYPE) { + gint i; + gchar *desc, *tmp; + gboolean has_parameters = FALSE; + + GstValidateActionParameter playback_time_param = { + .name = "playback-time", + .description = + "The playback time at which the action " "will be executed", + .mandatory = FALSE, + .types = "double,string", + .possible_variables = + "position: The current position in the stream\n" + "duration: The duration of the stream", + .def = "0.0" + }; + + GstValidateActionType *type = GST_VALIDATE_ACTION_TYPE (source); + + g_string_printf (string, "\nAction type:"); + g_string_append_printf (string, "\n Name: %s", type->name); + g_string_append_printf (string, "\n Implementer namespace: %s", + type->implementer_namespace); + + if (IS_CONFIG_ACTION_TYPE (type->flags)) + g_string_append_printf (string, + "\n Is config action (meaning it will be executing right " + "at the begining of the execution of the pipeline)"); + + + tmp = g_strdup_printf ("\n "); + desc = + g_regex_replace (newline_regex, type->description, -1, 0, tmp, 0, + NULL); + g_string_append_printf (string, "\n\n Description: \n %s", desc); + g_free (desc); + g_free (tmp); + + if (!IS_CONFIG_ACTION_TYPE (type->flags)) + print_action_parametter (string, type, &playback_time_param); + + if (type->parameters) { + has_parameters = TRUE; + g_string_append_printf (string, "\n\n Parametters:"); + for (i = 0; type->parameters[i].name; i++) { + print_action_parametter (string, type, &type->parameters[i]); + } + + } + + if ((type->flags & GST_VALIDATE_ACTION_TYPE_CAN_BE_OPTIONAL)) { + has_parameters = TRUE; + g_string_append_printf (string, "\n %-26s : %s", "optional", + "Don't raise an error if this action hasn't been executed of failed"); + g_string_append_printf (string, "\n %-28s %s", "", + "Possible types:"); + g_string_append_printf (string, "\n %-31s %s", "", "boolean"); + g_string_append_printf (string, "\n %-28s %s", "", + "Default: false"); + } + + if (!has_parameters) + g_string_append_printf (string, "\n\n No Parameters"); + } else if (GST_IS_VALIDATE_REPORTER (source) && + gst_validate_reporter_get_name (source)) { + g_string_printf (string, "\n%s --> ", + gst_validate_reporter_get_name (source)); + } else if (GST_IS_OBJECT (source)) { + g_string_printf (string, "\n%s --> ", GST_OBJECT_NAME (source)); + } else if (G_IS_OBJECT (source)) { + g_string_printf (string, "\n<%s@%p> --> ", G_OBJECT_TYPE_NAME (source), + source); + } + } + + g_string_append_vprintf (string, format, args); + + if (!newline_regex) + newline_regex = + g_regex_new ("\n", G_REGEX_OPTIMIZE | G_REGEX_MULTILINE, 0, NULL); + +#ifndef GST_DISABLE_GST_DEBUG + { + gchar *str; + + str = g_regex_replace (newline_regex, string->str, string->len, 0, + "", 0, NULL); + + if (source) + GST_INFO ("%s", str); + else + GST_DEBUG ("%s", str); + + g_free (str); + } +#endif + + for (i = 0; log_files[i]; i++) { + fprintf (log_files[i], "%s", string->str); + fflush (log_files[i]); + } + +out: + g_string_free (string, TRUE); +} + +gboolean +gst_validate_report_set_master_report (GstValidateReport * report, + GstValidateReport * master_report) +{ + GList *tmp; + gboolean add_shadow_report = TRUE; + + if (master_report->reporting_level >= GST_VALIDATE_SHOW_MONITOR) + return FALSE; + + report->master_report = master_report; + + GST_VALIDATE_REPORT_SHADOW_REPORTS_LOCK (master_report); + for (tmp = master_report->shadow_reports; tmp; tmp = tmp->next) { + GstValidateReport *shadow_report = (GstValidateReport *) tmp->data; + if (report->reporter == shadow_report->reporter) { + add_shadow_report = FALSE; + break; + } + } + if (add_shadow_report) + master_report->shadow_reports = + g_list_append (master_report->shadow_reports, + gst_validate_report_ref (report)); + GST_VALIDATE_REPORT_SHADOW_REPORTS_UNLOCK (master_report); + + return TRUE; +} + +void +gst_validate_report_print_level (GstValidateReport * report) +{ + gst_validate_printf (NULL, "%10s : %s\n", + gst_validate_report_level_get_name (report->level), + report->issue->summary); +} + +void +gst_validate_report_print_detected_on (GstValidateReport * report) +{ + GList *tmp; + + gst_validate_printf (NULL, "%*s Detected on <%s", + 12, "", gst_validate_reporter_get_name (report->reporter)); + for (tmp = report->shadow_reports; tmp; tmp = tmp->next) { + GstValidateReport *shadow_report = (GstValidateReport *) tmp->data; + gst_validate_printf (NULL, ", %s", + gst_validate_reporter_get_name (shadow_report->reporter)); + } + gst_validate_printf (NULL, ">\n"); +} + +void +gst_validate_report_print_details (GstValidateReport * report) +{ + if (report->message) + gst_validate_printf (NULL, "%*s Details : %s\n", 12, "", report->message); +} + +void +gst_validate_report_print_description (GstValidateReport * report) +{ + if (report->issue->description) + gst_validate_printf (NULL, "%*s Description : %s\n", 12, "", + report->issue->description); +} + +void +gst_validate_report_printf (GstValidateReport * report) +{ + GList *tmp; + + gst_validate_report_print_level (report); + gst_validate_report_print_detected_on (report); + gst_validate_report_print_details (report); + + for (tmp = report->repeated_reports; tmp; tmp = tmp->next) { + gst_validate_report_print_details (report); + } + + gst_validate_report_print_description (report); + gst_validate_printf (NULL, "\n"); +} + +void +gst_validate_report_set_reporting_level (GstValidateReport * report, + GstValidateReportingDetails level) +{ + report->reporting_level = level; +} + +void +gst_validate_report_add_repeated_report (GstValidateReport * report, + GstValidateReport * repeated_report) +{ + report->repeated_reports = + g_list_append (report->repeated_reports, + gst_validate_report_ref (repeated_report)); +} diff --git a/validate/gst/validate/gst-validate-report.h b/validate/gst/validate/gst-validate-report.h new file mode 100644 index 0000000..807a217 --- /dev/null +++ b/validate/gst/validate/gst-validate-report.h @@ -0,0 +1,239 @@ +/* GStreamer + * Copyright (C) 2013 Thiago Santos + * + * gst-validate-monitor-report.h - Validate Element report structures and functions + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_VALIDATE_REPORT_H__ +#define __GST_VALIDATE_REPORT_H__ + +#include + +typedef struct _GstValidateReport GstValidateReport; +typedef guintptr GstValidateIssueId; + +#include +#include +#include "gst-validate-types.h" + +G_BEGIN_DECLS + +GType gst_validate_report_get_type (void); +#define GST_TYPE_VALIDATE_REPORT (gst_validate_report_get_type ()) + +typedef enum { + GST_VALIDATE_FATAL_DEFAULT = 0, + GST_VALIDATE_FATAL_ISSUES = 1 << 0, + GST_VALIDATE_FATAL_WARNINGS = 1 << 1, + GST_VALIDATE_FATAL_CRITICALS = 1 << 2, + GST_VALIDATE_PRINT_ISSUES = 1 << 3, + GST_VALIDATE_PRINT_WARNINGS = 1 << 4, + GST_VALIDATE_PRINT_CRITICALS = 1 << 5 +} GstValidateDebugFlags; + +typedef enum { + GST_VALIDATE_REPORT_LEVEL_CRITICAL, + GST_VALIDATE_REPORT_LEVEL_WARNING, + GST_VALIDATE_REPORT_LEVEL_ISSUE, + GST_VALIDATE_REPORT_LEVEL_IGNORE, + GST_VALIDATE_REPORT_LEVEL_UNKNOWN, + GST_VALIDATE_REPORT_LEVEL_NUM_ENTRIES, +} GstValidateReportLevel; + +#define _QUARK g_quark_from_static_string + +#define BUFFER_BEFORE_SEGMENT _QUARK("buffer::before-segment") +#define BUFFER_IS_OUT_OF_SEGMENT _QUARK("buffer::is-out-of-segment") +#define BUFFER_TIMESTAMP_OUT_OF_RECEIVED_RANGE _QUARK("buffer::timestamp-out-of-received-range") +#define WRONG_FLOW_RETURN _QUARK("buffer::wrong-flow-return") +#define BUFFER_AFTER_EOS _QUARK("buffer::after-eos") +#define WRONG_BUFFER _QUARK("buffer::not-expected-one") +#define FLOW_ERROR_WITHOUT_ERROR_MESSAGE _QUARK("buffer::flow-error-without-error-message") + +#define CAPS_IS_MISSING_FIELD _QUARK("caps::is-missing-field") +#define CAPS_FIELD_HAS_BAD_TYPE _QUARK("caps::field-has-bad-type") +#define CAPS_EXPECTED_FIELD_NOT_FOUND _QUARK("caps::expected-field-not-found") +#define GET_CAPS_NOT_PROXYING_FIELDS _QUARK("caps::not-proxying-fields") +#define CAPS_FIELD_UNEXPECTED_VALUE _QUARK("caps::field-unexpected-value") + +#define EVENT_NEWSEGMENT_NOT_PUSHED _QUARK("event::newsegment-not-pushed") +#define SERIALIZED_EVENT_WASNT_PUSHED_IN_TIME _QUARK("event::serialized-event-wasnt-pushed-in-time") + +#define EOS_HAS_WRONG_SEQNUM _QUARK("event::eos-has-wrong-seqnum") +#define FLUSH_START_HAS_WRONG_SEQNUM _QUARK("event::flush-start-has-wrong-seqnum") +#define FLUSH_STOP_HAS_WRONG_SEQNUM _QUARK("event::flush-stop-has-wrong-seqnum") +#define SEGMENT_HAS_WRONG_SEQNUM _QUARK("event::segment-has-wrong-seqnum") + + +#define EVENT_SERIALIZED_OUT_OF_ORDER _QUARK("event::serialized-out-of-order") +#define EVENT_NEW_SEGMENT_MISMATCH _QUARK("event::segment-mismatch") +#define EVENT_FLUSH_START_UNEXPECTED _QUARK("event::flush-start-unexpected") +#define EVENT_FLUSH_STOP_UNEXPECTED _QUARK("event::flush-stop-unexpected") +#define EVENT_CAPS_DUPLICATE _QUARK("event::caps-duplicate") +#define EVENT_SEEK_NOT_HANDLED _QUARK("event::seek-not-handled") +#define EVENT_SEEK_RESULT_POSITION_WRONG _QUARK("event::seek-result-position-wrong") +#define EVENT_EOS_WITHOUT_SEGMENT _QUARK("event::eos-without-segment") + +#define STATE_CHANGE_FAILURE _QUARK("state::change-failure") + +#define FILE_NO_STREAM_ID _QUARK("file-checking::no-stream-id") +#define FILE_TAG_DETECTION_INCORRECT _QUARK("file-checking::tag-detection-incorrect") +#define FILE_SIZE_INCORRECT _QUARK("file-checking::size-incorrect") +#define FILE_DURATION_INCORRECT _QUARK("file-checking::duration-incorrect") +#define FILE_SEEKABLE_INCORRECT _QUARK("file-checking::seekable-incorrect") +#define FILE_PROFILE_INCORRECT _QUARK("file-checking::profile-incorrect") + +#define ALLOCATION_FAILURE _QUARK("runtime::allocation-failure") +#define MISSING_PLUGIN _QUARK("runtime::missing-plugin") +#define WARNING_ON_BUS _QUARK("runtime::warning-on-bus") +#define ERROR_ON_BUS _QUARK("runtime::error-on-bus") + +#define QUERY_POSITION_SUPERIOR_DURATION _QUARK("query::position-superior-duration") +#define QUERY_POSITION_OUT_OF_SEGMENT _QUARK("query::position-out-of-segment") + +#define SCENARIO_NOT_ENDED _QUARK("scenario::not-ended") +#define SCENARIO_FILE_MALFORMED _QUARK("scenario::malformed") +#define SCENARIO_ACTION_EXECUTION_ERROR _QUARK("scenario::execution-error") +#define SCENARIO_ACTION_EXECUTION_ISSUE _QUARK("scenario::execution-issue") + +#define G_LOG_ISSUE _QUARK("g-log::issue") +#define G_LOG_WARNING _QUARK("g-log::warning") +#define G_LOG_CRITICAL _QUARK("g-log::critical") + +typedef struct { + GstValidateIssueId issue_id; + + /* Summary: one-liner translatable description of the issue */ + gchar *summary; + /* description: multi-line translatable description of: + * * what the issue is (and why it's an issue) + * * what the source problem could be + * * pointers to fixing the issue + */ + gchar *description; + + /* The name of the area of issue + * this one is in */ + gchar *area; + /* The name of the issue type */ + gchar *name; + + /* default_level: The default level of severity for this + * issue. */ + GstValidateReportLevel default_level; + + gint refcount; + + gpointer _gst_reserved[GST_PADDING]; + +} GstValidateIssue; + +GType gst_validate_issue_get_type (void); + +struct _GstValidateReport { + gint refcount; + + /* issue: The issue this report corresponds to (to get description, summary,...) */ + GstValidateIssue *issue; + + GstValidateReportLevel level; + + /* The reporter that reported the issue (to get names, info, ...) */ + GstValidateReporter *reporter; + + /* timestamp: The time at which this issue happened since + * the process start (to stay in sync with gst logging) */ + GstClockTime timestamp; + + /* message: issue-specific message. Gives more detail on the actual + * issue. Can be NULL */ + gchar *message; + + /* When reporter->intercept_report returns KEEP, the report is not + * added to the runner. It can be added as a "shadow_report" to + * the upstream report, which is tracked by the runner. */ + GMutex shadow_reports_lock; + GstValidateReport *master_report; + GList *shadow_reports; + + /* Lists the reports that were repeated inside the same reporter */ + GList *repeated_reports; + + GstValidateReportingDetails reporting_level; + + gpointer _gst_reserved[GST_PADDING]; +}; + +void gst_validate_report_add_message (GstValidateReport *report, + const gchar *message); + +#define GST_VALIDATE_ISSUE_FORMAT G_GUINTPTR_FORMAT " (%s) : %s: %s" +#define GST_VALIDATE_ISSUE_ARGS(i) gst_validate_issue_get_id (i), \ + gst_validate_report_level_get_name (i->default_level), \ + i->area, \ + i->summary + +#define GST_VALIDATE_ERROR_REPORT_PRINT_FORMAT GST_TIME_FORMAT " <%s>: %" GST_VALIDATE_ISSUE_FORMAT ": %s" +#define GST_VALIDATE_REPORT_PRINT_ARGS(r) GST_TIME_ARGS (r->timestamp), \ + gst_validate_reporter_get_name (r->reporter), \ + GST_VALIDATE_ISSUE_ARGS (r->issue), \ + r->message +void gst_validate_report_init (void); +GstValidateIssue *gst_validate_issue_from_id (GstValidateIssueId issue_id); +GstValidateIssueId gst_validate_issue_get_id (GstValidateIssue * issue); +void gst_validate_issue_register (GstValidateIssue * issue); +GstValidateIssue *gst_validate_issue_new (GstValidateIssueId issue_id, const gchar * summary, + const gchar * description, + GstValidateReportLevel default_level); +void gst_validate_issue_set_default_level (GstValidateIssue *issue, + GstValidateReportLevel default_level); + +GstValidateReport *gst_validate_report_new (GstValidateIssue * issue, + GstValidateReporter * reporter, + const gchar * message); +void gst_validate_report_unref (GstValidateReport * report); +GstValidateReport *gst_validate_report_ref (GstValidateReport * report); + +GstValidateIssueId gst_validate_report_get_issue_id (GstValidateReport * report); + +gboolean gst_validate_report_check_abort (GstValidateReport * report); +void gst_validate_report_printf (GstValidateReport * report); +void gst_validate_report_print_level (GstValidateReport *report); +void gst_validate_report_print_detected_on (GstValidateReport *report); +void gst_validate_report_print_details (GstValidateReport *report); +void gst_validate_report_print_description (GstValidateReport *report); + +const gchar * gst_validate_report_level_get_name (GstValidateReportLevel level); + +void gst_validate_printf (gpointer source, + const gchar * format, + ...) G_GNUC_PRINTF (2, 3) G_GNUC_NO_INSTRUMENT; +void gst_validate_print_action (GstValidateAction *action, const gchar * message); +void gst_validate_printf_valist (gpointer source, + const gchar * format, + va_list args) G_GNUC_NO_INSTRUMENT; +gboolean gst_validate_report_should_print (GstValidateReport * report); +gboolean gst_validate_report_set_master_report(GstValidateReport *report, GstValidateReport *master_report); +void gst_validate_report_set_reporting_level (GstValidateReport *report, GstValidateReportingDetails level); +void gst_validate_report_add_repeated_report (GstValidateReport *report, GstValidateReport *repeated_report); +GstValidateReportLevel gst_validate_report_level_from_name (const gchar *issue_name); + +G_END_DECLS + +#endif /* __GST_VALIDATE_REPORT_H__ */ + diff --git a/validate/gst/validate/gst-validate-reporter.c b/validate/gst/validate/gst-validate-reporter.c new file mode 100644 index 0000000..a34476d --- /dev/null +++ b/validate/gst/validate/gst-validate-reporter.c @@ -0,0 +1,408 @@ +/* GStreamer + * + * Copyright (C) 2013 Thibault Saunier + * + * gst-validate-reporter.c + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/** + * SECTION:gst-validate-reporter + * @short_description: A #GInterface that allows #GObject to be used as originator of + * issues in the GstValidate reporting system + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "gst-validate-internal.h" +#include "gst-validate-reporter.h" +#include "gst-validate-report.h" + +#define REPORTER_PRIVATE "gst-validate-reporter-private" + +typedef struct _GstValidateReporterPrivate +{ + GstValidateRunner *runner; + GHashTable *reports; + char *name; + guint log_handler_id; + GMutex reports_lock; +} GstValidateReporterPrivate; + +static GstValidateReporterPrivate *g_log_handler = NULL; + +G_DEFINE_INTERFACE (GstValidateReporter, gst_validate_reporter, G_TYPE_OBJECT); + +static void +gst_validate_reporter_default_init (GstValidateReporterInterface * iface) +{ + g_object_interface_install_property (iface, + g_param_spec_object ("validate-runner", "Validate Runner", + "The Validate runner to " "report errors to", + GST_TYPE_VALIDATE_RUNNER, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); +} + +static void +_free_priv (GstValidateReporterPrivate * priv) +{ + + if (g_log_handler == priv) { + g_log_set_default_handler (g_log_default_handler, NULL); + g_log_handler = NULL; + } + + g_hash_table_unref (priv->reports); + g_free (priv->name); + g_mutex_clear (&priv->reports_lock); + g_slice_free (GstValidateReporterPrivate, priv); +} + +static GstValidateReporterPrivate * +gst_validate_reporter_get_priv (GstValidateReporter * reporter) +{ + GstValidateReporterPrivate *priv; + + priv = g_object_get_data (G_OBJECT (reporter), REPORTER_PRIVATE); + + if (priv == NULL) { + priv = g_slice_new0 (GstValidateReporterPrivate); + priv->reports = g_hash_table_new_full (g_direct_hash, + g_direct_equal, NULL, (GDestroyNotify) gst_validate_report_unref); + + g_mutex_init (&priv->reports_lock); + g_object_set_data_full (G_OBJECT (reporter), REPORTER_PRIVATE, priv, + (GDestroyNotify) _free_priv); + } + + return priv; +} + +#define GST_VALIDATE_REPORTER_REPORTS_LOCK(r) \ + G_STMT_START { \ + (g_mutex_lock (&gst_validate_reporter_get_priv(GST_VALIDATE_REPORTER_CAST(r))->reports_lock)); \ + } G_STMT_END + +#define GST_VALIDATE_REPORTER_REPORTS_UNLOCK(r) \ + G_STMT_START { \ + (g_mutex_unlock (&gst_validate_reporter_get_priv(GST_VALIDATE_REPORTER_CAST(r))->reports_lock)); \ + } G_STMT_END + +static GstValidateInterceptionReturn +gst_validate_reporter_intercept_report (GstValidateReporter * reporter, + GstValidateReport * report) +{ + GstValidateInterceptionReturn ret = GST_VALIDATE_REPORTER_REPORT; + GstValidateReporterInterface *iface = + GST_VALIDATE_REPORTER_GET_INTERFACE (reporter); + + if (iface->intercept_report) { + ret = iface->intercept_report (reporter, report); + } + + return ret; +} + +GstValidateReportingDetails +gst_validate_reporter_get_reporting_level (GstValidateReporter * reporter) +{ + GstValidateReportingDetails ret = GST_VALIDATE_SHOW_UNKNOWN; + GstValidateReporterInterface *iface = + GST_VALIDATE_REPORTER_GET_INTERFACE (reporter); + + if (iface->get_reporting_level) { + ret = iface->get_reporting_level (reporter); + } + + return ret; +} + +GstValidateReport * +gst_validate_reporter_get_report (GstValidateReporter * reporter, + GstValidateIssueId issue_id) +{ + GstValidateReport *report; + GstValidateReporterPrivate *priv = gst_validate_reporter_get_priv (reporter); + + GST_VALIDATE_REPORTER_REPORTS_LOCK (reporter); + report = g_hash_table_lookup (priv->reports, (gconstpointer) issue_id); + GST_VALIDATE_REPORTER_REPORTS_UNLOCK (reporter); + + return report; +} + +void +gst_validate_report_valist (GstValidateReporter * reporter, + GstValidateIssueId issue_id, const gchar * format, va_list var_args) +{ + GstValidateReport *report, *prev_report; + gchar *message, *combo; + va_list vacopy; + GstValidateIssue *issue; + GstValidateReporterPrivate *priv = gst_validate_reporter_get_priv (reporter); + GstValidateInterceptionReturn int_ret; + + issue = gst_validate_issue_from_id (issue_id); + + g_return_if_fail (issue != NULL); + g_return_if_fail (GST_IS_VALIDATE_REPORTER (reporter)); + + G_VA_COPY (vacopy, var_args); + message = g_strdup_vprintf (format, vacopy); + report = gst_validate_report_new (issue, reporter, message); + +#ifndef GST_DISABLE_GST_DEBUG + combo = + g_strdup_printf ("<%s> %" GST_VALIDATE_ISSUE_FORMAT " : %s", priv->name, + GST_VALIDATE_ISSUE_ARGS (issue), format); + G_VA_COPY (vacopy, var_args); + if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL) { + gst_debug_log_valist (GST_CAT_DEFAULT, GST_LEVEL_ERROR, __FILE__, + GST_FUNCTION, __LINE__, NULL, combo, vacopy); + } else if (report->level == GST_VALIDATE_REPORT_LEVEL_WARNING) + gst_debug_log_valist (GST_CAT_DEFAULT, GST_LEVEL_WARNING, __FILE__, + GST_FUNCTION, __LINE__, NULL, combo, vacopy); + else if (report->level == GST_VALIDATE_REPORT_LEVEL_ISSUE) + gst_debug_log_valist (GST_CAT_DEFAULT, GST_LEVEL_LOG, __FILE__, + GST_FUNCTION, __LINE__, (GObject *) NULL, combo, vacopy); + else + gst_debug_log_valist (GST_CAT_DEFAULT, GST_LEVEL_DEBUG, __FILE__, + GST_FUNCTION, __LINE__, NULL, combo, vacopy); + g_free (combo); +#endif + + int_ret = gst_validate_reporter_intercept_report (reporter, report); + + if (int_ret == GST_VALIDATE_REPORTER_DROP) { + gst_validate_report_unref (report); + goto done; + } + + prev_report = g_hash_table_lookup (priv->reports, (gconstpointer) issue_id); + + if (prev_report) { + GstValidateReportingDetails reporter_level = + gst_validate_reporter_get_reporting_level (reporter); + GstValidateReportingDetails runner_level = GST_VALIDATE_SHOW_UNKNOWN; + + if (priv->runner) + runner_level = + gst_validate_runner_get_default_reporting_level (priv->runner); + + if (reporter_level == GST_VALIDATE_SHOW_ALL || + (runner_level == GST_VALIDATE_SHOW_ALL && + reporter_level == GST_VALIDATE_SHOW_UNKNOWN)) + gst_validate_report_add_repeated_report (prev_report, report); + + gst_validate_report_unref (report); + goto done; + } + + GST_VALIDATE_REPORTER_REPORTS_LOCK (reporter); + g_hash_table_insert (priv->reports, (gpointer) issue_id, report); + GST_VALIDATE_REPORTER_REPORTS_UNLOCK (reporter); + + if (priv->runner && int_ret == GST_VALIDATE_REPORTER_REPORT) { + gst_validate_runner_add_report (priv->runner, report); + } + + if (gst_validate_report_check_abort (report)) { + if (priv->runner) + gst_validate_runner_printf (priv->runner); + + g_error ("Fatal report received: %" GST_VALIDATE_ERROR_REPORT_PRINT_FORMAT, + GST_VALIDATE_REPORT_PRINT_ARGS (report)); + } + +done: + g_free (message); +} + +static void +gst_validate_reporter_g_log_func (const gchar * log_domain, + GLogLevelFlags log_level, const gchar * message, + GstValidateReporter * reporter) +{ + if (log_level & G_LOG_LEVEL_CRITICAL) + GST_VALIDATE_REPORT (reporter, G_LOG_CRITICAL, "%s", message); + else if (log_level & G_LOG_LEVEL_WARNING) + GST_VALIDATE_REPORT (reporter, G_LOG_WARNING, "%s", message); + else + GST_VALIDATE_REPORT (reporter, G_LOG_ISSUE, "%s", message); +} + +/** + * gst_validate_report: + * @report: The source of the new report + * @issue_id: The #GstValidateIssueId of the issue + * @format: The format of the message describing the issue in a printf + * format followed by the parametters. + * + * Reports a new issue in the GstValidate reporting system with @m + * as the source of that issue. + * + * You can also use #GST_VALIDATE_REPORT instead. + */ +void +gst_validate_report (GstValidateReporter * reporter, + GstValidateIssueId issue_id, const gchar * format, ...) +{ + va_list var_args; + + va_start (var_args, format); + gst_validate_report_valist (reporter, issue_id, format, var_args); + va_end (var_args); +} + +void +gst_validate_reporter_report_simple (GstValidateReporter * reporter, + GstValidateIssueId issue_id, const gchar * message) +{ + gst_validate_report (reporter, issue_id, "%s", message); +} + +/** + * gst_validate_reporter_set_name: + * @reporter: The reporter to set the name on + * @name: (transfer full): The name of the reporter + * + * Sets @ name on @reporter + */ +void +gst_validate_reporter_set_name (GstValidateReporter * reporter, gchar * name) +{ + GstValidateReporterPrivate *priv = gst_validate_reporter_get_priv (reporter); + + if (priv->name) + g_free (priv->name); + + priv->name = name; +} + +const gchar * +gst_validate_reporter_get_name (GstValidateReporter * reporter) +{ + GstValidateReporterPrivate *priv = gst_validate_reporter_get_priv (reporter); + + return priv->name; +} + +GstValidateRunner * +gst_validate_reporter_get_runner (GstValidateReporter * reporter) +{ + GstValidateReporterPrivate *priv = gst_validate_reporter_get_priv (reporter); + + return priv->runner; +} + +void +gst_validate_reporter_set_runner (GstValidateReporter * reporter, + GstValidateRunner * runner) +{ + GstValidateReporterPrivate *priv = gst_validate_reporter_get_priv (reporter); + + priv->runner = runner; + + g_object_notify (G_OBJECT (reporter), "validate-runner"); +} + +/** + * gst_validate_reporter_set_handle_g_logs: + * @reporter: The #GstValidateReporter to set has the handler for g_log + * + * Set @reporter has the 'source' of any g_log happening during the + * execution. Usually the monitor of the first #GstPipeline is used + * to handle g_logs. + * + * Basically this function is used in order to start tracking any + * issue reported with g_log in the process into GstValidate report + * in the GstValidate reporting system. + */ +void +gst_validate_reporter_set_handle_g_logs (GstValidateReporter * reporter) +{ + g_log_set_default_handler ((GLogFunc) gst_validate_reporter_g_log_func, + reporter); + + g_log_set_handler ("GStreamer", + G_LOG_LEVEL_MASK, (GLogFunc) gst_validate_reporter_g_log_func, reporter); + + g_log_set_handler ("GLib", + G_LOG_LEVEL_MASK, (GLogFunc) gst_validate_reporter_g_log_func, reporter); + + + g_log_set_handler ("GLib-GObject", + G_LOG_LEVEL_MASK, (GLogFunc) gst_validate_reporter_g_log_func, reporter); + + g_log_handler = gst_validate_reporter_get_priv (reporter); +} + +/** + * gst_validate_reporter_get_reports: + * @reporter: a #GstValidateReporter + * + * Get the list of reports present in the reporter. + * + * Returns: (transfer full) (element-type GstValidateReport): the list of + * #GstValidateReport present in the reporter. + * The caller should unref each report once it is done with them. + */ +GList * +gst_validate_reporter_get_reports (GstValidateReporter * reporter) +{ + GstValidateReporterPrivate *priv; + GList *reports, *tmp; + GList *ret = NULL; + + priv = g_object_get_data (G_OBJECT (reporter), REPORTER_PRIVATE); + + GST_VALIDATE_REPORTER_REPORTS_LOCK (reporter); + reports = g_hash_table_get_values (priv->reports); + for (tmp = reports; tmp; tmp = tmp->next) { + ret = + g_list_append (ret, + gst_validate_report_ref ((GstValidateReport *) (tmp->data))); + } + g_list_free (reports); + GST_VALIDATE_REPORTER_REPORTS_UNLOCK (reporter); + + return ret; +} + +/** + * gst_validate_reporter_get_reports_count: + * @reporter: a #GstValidateReporter + * + * Get the number of reports present in the reporter. + * + * Returns: the number of reports currently present in @reporter. + */ +gint +gst_validate_reporter_get_reports_count (GstValidateReporter * reporter) +{ + GstValidateReporterPrivate *priv; + gint ret; + + priv = g_object_get_data (G_OBJECT (reporter), REPORTER_PRIVATE); + + GST_VALIDATE_REPORTER_REPORTS_LOCK (reporter); + ret = g_hash_table_size (priv->reports); + GST_VALIDATE_REPORTER_REPORTS_UNLOCK (reporter); + + return ret; +} diff --git a/validate/gst/validate/gst-validate-reporter.h b/validate/gst/validate/gst-validate-reporter.h new file mode 100644 index 0000000..108a273 --- /dev/null +++ b/validate/gst/validate/gst-validate-reporter.h @@ -0,0 +1,119 @@ +/* GStreamer + * + * Copyright (C) 2013 Thibault Saunier + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef _GST_VALIDATE_REPORTER_ +#define _GST_VALIDATE_REPORTER_ + +typedef struct _GstValidateReporter GstValidateReporter; +typedef struct _GstValidateReporterInterface GstValidateReporterInterface; + +#include +#include +#include +#include + +G_BEGIN_DECLS + +/* GstValidateReporter interface declarations */ +#define GST_TYPE_VALIDATE_REPORTER (gst_validate_reporter_get_type ()) +#define GST_VALIDATE_REPORTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_VALIDATE_REPORTER, GstValidateReporter)) +#define GST_IS_VALIDATE_REPORTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_VALIDATE_REPORTER)) +#define GST_VALIDATE_REPORTER_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), GST_TYPE_VALIDATE_REPORTER, GstValidateReporterInterface)) +#define GST_VALIDATE_REPORTER_CAST(obj) ((GstValidateReporter *) obj) + +/** + * GST_VALIDATE_REPORT: + * @m: The #GstValidateReporter where the issue happened + * @issue_id: The #GstValidateIssueId of the issue + * @...: The format of the message describing the issue in a printf + * format, followed by the parametters. + * + * Reports a new issue in the GstValidate reporting system with @m + * as the source of that issue. + */ +#ifdef G_HAVE_ISO_VARARGS +#define GST_VALIDATE_REPORT(m, issue_id, ...) \ + G_STMT_START { \ + gst_validate_report (GST_VALIDATE_REPORTER (m), \ + issue_id, \ + __VA_ARGS__ ); \ + } G_STMT_END + +#else /* G_HAVE_GNUC_VARARGS */ +#ifdef G_HAVE_GNUC_VARARGS +#define GST_VALIDATE_REPORT(m, issue_id, args...) \ + G_STMT_START { \ + gst_validate_report (GST_VALIDATE_REPORTER (m), \ + issue_id, ##args ); \ + } G_STMT_END +#endif /* G_HAVE_ISO_VARARGS */ +#endif /* G_HAVE_GNUC_VARARGS */ +GType gst_validate_reporter_get_type (void); + +/** + * GstValidateInterceptionReturn: + * @GST_VALIDATE_REPORTER_DROP: The report will be completely ignored. + * @GST_VALIDATE_REPORTER_KEEP: The report will be kept by the reporter, + * but not reported to the runner. + * @GST_VALIDATE_REPORTER_REPORT: The report will be kept by the reporter + * and reported to the runner. + */ +typedef enum +{ + GST_VALIDATE_REPORTER_DROP, + GST_VALIDATE_REPORTER_KEEP, + GST_VALIDATE_REPORTER_REPORT +} GstValidateInterceptionReturn; + +/** + * GstValidateReporter: + */ +struct _GstValidateReporterInterface +{ + GTypeInterface parent; + + GstValidateInterceptionReturn (*intercept_report) (GstValidateReporter * + reporter, GstValidateReport * report); + GstValidateReportingDetails (*get_reporting_level) (GstValidateReporter * + reporter); +}; + +void gst_validate_reporter_set_name (GstValidateReporter * reporter, + gchar * name); +const gchar * gst_validate_reporter_get_name (GstValidateReporter * reporter); +GstValidateRunner * gst_validate_reporter_get_runner (GstValidateReporter *reporter); +void gst_validate_reporter_init (GstValidateReporter * reporter, const gchar *name); +void gst_validate_report (GstValidateReporter * reporter, GstValidateIssueId issue_id, + const gchar * format, ...) G_GNUC_PRINTF (3, 4) G_GNUC_NO_INSTRUMENT; +void gst_validate_report_valist (GstValidateReporter * reporter, GstValidateIssueId issue_id, + const gchar * format, va_list var_args); +void +gst_validate_reporter_report_simple (GstValidateReporter * reporter, GstValidateIssueId issue_id, + const gchar * message); + +void gst_validate_reporter_set_runner (GstValidateReporter * reporter, GstValidateRunner *runner); +void gst_validate_reporter_set_handle_g_logs (GstValidateReporter * reporter); +GstValidateReport * gst_validate_reporter_get_report (GstValidateReporter *reporter, + GstValidateIssueId issue_id); +GList * gst_validate_reporter_get_reports (GstValidateReporter * reporter); +gint gst_validate_reporter_get_reports_count (GstValidateReporter *reporter); +GstValidateReportingDetails gst_validate_reporter_get_reporting_level (GstValidateReporter *reporter); + +G_END_DECLS +#endif /* _GST_VALIDATE_REPORTER_ */ diff --git a/validate/gst/validate/gst-validate-runner.c b/validate/gst/validate/gst-validate-runner.c new file mode 100644 index 0000000..afbcb9f --- /dev/null +++ b/validate/gst/validate/gst-validate-runner.c @@ -0,0 +1,531 @@ +/* GStreamer + * + * Copyright (C) 2013 Collabora Ltd. + * Author: Thiago Sousa Santos + * + * gst-validate-runner.c - Validate Runner class + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "gst-validate-internal.h" +#include "gst-validate-report.h" +#include "gst-validate-monitor-factory.h" +#include "gst-validate-override-registry.h" +#include "gst-validate-runner.h" + +/** + * SECTION:gst-validate-runner + * @short_description: Class that runs Gst Validate tests for a pipeline + * + * Allows you to test a pipeline within GstValidate. It is the object where + * all issue reporting is done. + * + * In the tools using GstValidate the only minimal code to be able to monitor + * your pipelines is: + * + * |[ + * GstPipeline *pipeline = gst_pipeline_new ("monitored-pipeline"); + * GstValidateRunner *runner = gst_validate_runner_new (); + * GstValidateMonitor *monitor = gst_validate_monitor_factory_create ( + * GST_OBJECT (pipeline), runner, NULL); + * + * // Run the pipeline and do whatever you want with it + * + * // In that same order + * gst_object_unref (pipeline); + * gst_object_unref (runner); + * gst_object_unref (monitor); + * ]| + */ + +struct _GstValidateRunnerPrivate +{ + GMutex mutex; + GList *reports; + GstValidateReportingDetails default_level; + GHashTable *reports_by_type; + + /* A list of PatternLevel */ + GList *report_pattern_levels; +}; + +/* Describes the reporting level to apply to a name pattern */ +typedef struct _PatternLevel +{ + GPatternSpec *pattern; + GstValidateReportingDetails level; +} PatternLevel; + +#define GST_VALIDATE_RUNNER_LOCK(r) \ + G_STMT_START { \ + GST_LOG_OBJECT (r, "About to lock %p", &GST_VALIDATE_RUNNER_CAST(r)->priv->mutex); \ + (g_mutex_lock (&GST_VALIDATE_RUNNER_CAST(r)->priv->mutex)); \ + GST_LOG_OBJECT (r, "Acquired lock %p", &GST_VALIDATE_RUNNER_CAST(r)->priv->mutex); \ + } G_STMT_END + +#define GST_VALIDATE_RUNNER_UNLOCK(r) \ + G_STMT_START { \ + GST_LOG_OBJECT (r, "About to unlock %p", &GST_VALIDATE_RUNNER_CAST(r)->priv->mutex); \ + (g_mutex_unlock (&GST_VALIDATE_RUNNER_CAST(r)->priv->mutex)); \ + GST_LOG_OBJECT (r, "Released lock %p", &GST_VALIDATE_RUNNER_CAST(r)->priv->mutex); \ + } G_STMT_END + +#define gst_validate_runner_parent_class parent_class +G_DEFINE_TYPE (GstValidateRunner, gst_validate_runner, G_TYPE_OBJECT); + +/* signals */ +enum +{ + REPORT_ADDED_SIGNAL, + STOPPING_SIGNAL, + /* add more above */ + LAST_SIGNAL +}; + +static guint _signals[LAST_SIGNAL] = { 0 }; + +static gboolean +_parse_reporting_level (gchar * str, GstValidateReportingDetails * level) +{ + if (!str) + return FALSE; + + /* works in place */ + g_strstrip (str); + + if (g_ascii_isdigit (str[0])) { + unsigned long l; + char *endptr; + l = strtoul (str, &endptr, 10); + if (endptr > str && endptr[0] == 0) { + *level = (GstValidateReportingDetails) l; + } else { + return FALSE; + } + } else if (g_ascii_strcasecmp (str, "none") == 0) { + *level = GST_VALIDATE_SHOW_NONE; + } else if (g_ascii_strcasecmp (str, "synthetic") == 0) { + *level = GST_VALIDATE_SHOW_SYNTHETIC; + } else if (g_ascii_strcasecmp (str, "subchain") == 0) { + *level = GST_VALIDATE_SHOW_SUBCHAIN; + } else if (g_ascii_strcasecmp (str, "monitor") == 0) { + *level = GST_VALIDATE_SHOW_MONITOR; + } else if (g_ascii_strcasecmp (str, "all") == 0) { + *level = GST_VALIDATE_SHOW_ALL; + } else + return FALSE; + + return TRUE; +} + +static void +_free_report_pattern_level (PatternLevel * pattern_level) +{ + g_pattern_spec_free (pattern_level->pattern); + g_free (pattern_level); +} + +static void +_set_reporting_level_for_name (GstValidateRunner * runner, + const gchar * pattern, GstValidateReportingDetails level) +{ + PatternLevel *pattern_level = g_malloc (sizeof (PatternLevel)); + GPatternSpec *pattern_spec = g_pattern_spec_new (pattern); + + pattern_level->pattern = pattern_spec; + pattern_level->level = level; + + /* Allow the user to single out a pad with the "element-name__pad-name" syntax + */ + if (g_strrstr (pattern, "__")) + runner->priv->report_pattern_levels = + g_list_prepend (runner->priv->report_pattern_levels, pattern_level); + else + runner->priv->report_pattern_levels = + g_list_append (runner->priv->report_pattern_levels, pattern_level); +} + +static void +_replace_double_colons (gchar * word) +{ + while (word) { + word = strstr (word, "::"); + if (word) { + word[0] = '_'; + word[1] = '_'; + } + } +} + +static void +_set_report_levels_from_string (GstValidateRunner * self, const gchar * list) +{ + gchar **split; + gchar **walk; + + g_assert (list); + + GST_DEBUG_OBJECT (self, "setting report levels from string [%s]", list); + + split = g_strsplit (list, ",", 0); + + for (walk = split; *walk; walk++) { + _replace_double_colons (*walk); + if (strchr (*walk, ':')) { + gchar **values = g_strsplit (*walk, ":", 2); + + if (values[0] && values[1]) { + GstValidateReportingDetails level; + + if (_parse_reporting_level (values[1], &level)) + _set_reporting_level_for_name (self, values[0], level); + } + + g_strfreev (values); + } else { + GstValidateReportingDetails level; + + if (_parse_reporting_level (*walk, &level)) + self->priv->default_level = level; + } + } + + g_strfreev (split); +} + +static void +_init_report_levels (GstValidateRunner * self) +{ + const gchar *env; + + env = g_getenv ("GST_VALIDATE_REPORTING_DETAILS"); + if (env) + _set_report_levels_from_string (self, env); +} + +static void +_unref_report_list (gpointer unused, GList * reports, gpointer unused_too) +{ + g_list_free_full (reports, (GDestroyNotify) gst_validate_report_unref); +} + +static void +gst_validate_runner_dispose (GObject * object) +{ + GstValidateRunner *runner = GST_VALIDATE_RUNNER_CAST (object); + + g_list_free_full (runner->priv->reports, + (GDestroyNotify) gst_validate_report_unref); + g_list_free_full (runner->priv->report_pattern_levels, + (GDestroyNotify) _free_report_pattern_level); + + g_mutex_clear (&runner->priv->mutex); + + g_hash_table_foreach (runner->priv->reports_by_type, (GHFunc) + _unref_report_list, NULL); + g_hash_table_destroy (runner->priv->reports_by_type); + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gst_validate_runner_class_init (GstValidateRunnerClass * klass) +{ + GObjectClass *gobject_class; + + gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->dispose = gst_validate_runner_dispose; + + g_type_class_add_private (klass, sizeof (GstValidateRunnerPrivate)); + + _signals[REPORT_ADDED_SIGNAL] = + g_signal_new ("report-added", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, + GST_TYPE_VALIDATE_REPORT); + + _signals[STOPPING_SIGNAL] = + g_signal_new ("stopping", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, + NULL, NULL, NULL, G_TYPE_NONE, 0); +} + +static void +gst_validate_runner_init (GstValidateRunner * runner) +{ + runner->priv = G_TYPE_INSTANCE_GET_PRIVATE (runner, GST_TYPE_VALIDATE_RUNNER, + GstValidateRunnerPrivate); + g_mutex_init (&runner->priv->mutex); + + runner->priv->reports_by_type = g_hash_table_new (g_direct_hash, + g_direct_equal); + + runner->priv->default_level = GST_VALIDATE_SHOW_DEFAULT; + _init_report_levels (runner); +} + +/** + * gst_validate_runner_new: + * + * Create a new #GstValidateRunner + * + * Returns: A newly created #GstValidateRunner + */ +GstValidateRunner * +gst_validate_runner_new (void) +{ + return g_object_new (GST_TYPE_VALIDATE_RUNNER, NULL); +} + +/* + * gst_validate_runner_get_default_reporting_level: + * + * Returns: the default #GstValidateReportingDetails used to output a report. + */ +GstValidateReportingDetails +gst_validate_runner_get_default_reporting_level (GstValidateRunner * runner) +{ + return runner->priv->default_level; +} + +/* + * gst_validate_runner_get_reporting_level_for_name: + * + * Returns: the #GstValidateReportingDetails that will be applied for a given name. + * If no pattern was set for such a name, this function will return + * #GST_VALIDATE_SHOW_UNKNOWN, and reporting for that name will + * default to the global reporting level. + */ +GstValidateReportingDetails +gst_validate_runner_get_reporting_level_for_name (GstValidateRunner * runner, + const gchar * name) +{ + GList *tmp; + + for (tmp = runner->priv->report_pattern_levels; tmp; tmp = tmp->next) { + PatternLevel *pattern_level = (PatternLevel *) tmp->data; + if (g_pattern_match_string (pattern_level->pattern, name)) + return pattern_level->level; + } + + return GST_VALIDATE_SHOW_UNKNOWN; +} + +static void +synthesize_reports (GstValidateRunner * runner, GstValidateReport * report) +{ + GstValidateIssueId issue_id; + GList *reports; + + issue_id = report->issue->issue_id; + + GST_VALIDATE_RUNNER_LOCK (runner); + reports = + g_hash_table_lookup (runner->priv->reports_by_type, + (gconstpointer) issue_id); + reports = g_list_append (reports, gst_validate_report_ref (report)); + g_hash_table_insert (runner->priv->reports_by_type, (gpointer) issue_id, + reports); + GST_VALIDATE_RUNNER_UNLOCK (runner); +} + +void +gst_validate_runner_add_report (GstValidateRunner * runner, + GstValidateReport * report) +{ + GstValidateReportingDetails reporter_level = + gst_validate_reporter_get_reporting_level (report->reporter); + + /* Let's use our own reporting strategy */ + if (reporter_level == GST_VALIDATE_SHOW_UNKNOWN) { + gst_validate_report_set_reporting_level (report, + runner->priv->default_level); + switch (runner->priv->default_level) { + case GST_VALIDATE_SHOW_NONE: + return; + case GST_VALIDATE_SHOW_SYNTHETIC: + synthesize_reports (runner, report); + return; + default: + break; + } + } + + GST_VALIDATE_RUNNER_LOCK (runner); + runner->priv->reports = + g_list_append (runner->priv->reports, gst_validate_report_ref (report)); + GST_VALIDATE_RUNNER_UNLOCK (runner); + + g_signal_emit (runner, _signals[REPORT_ADDED_SIGNAL], 0, report); +} + +/** + * gst_validate_runner_get_reports_count: + * @runner: The $GstValidateRunner to get the number of report from + * + * Get the number of reports present in the runner: + * + * Returns: The number of report present in the runner. + */ +guint +gst_validate_runner_get_reports_count (GstValidateRunner * runner) +{ + GList *tmp; + guint l; + + g_return_val_if_fail (runner != NULL, 0); + + GST_VALIDATE_RUNNER_LOCK (runner); + l = g_list_length (runner->priv->reports); + for (tmp = runner->priv->reports; tmp; tmp = tmp->next) + l += g_list_length (((GstValidateReport *) tmp->data)->repeated_reports); + l += g_hash_table_size (runner->priv->reports_by_type); + GST_VALIDATE_RUNNER_UNLOCK (runner); + + return l; +} + +GList * +gst_validate_runner_get_reports (GstValidateRunner * runner) +{ + GList *ret; + + GST_VALIDATE_RUNNER_LOCK (runner); + ret = + g_list_copy_deep (runner->priv->reports, + (GCopyFunc) gst_validate_report_ref, NULL); + GST_VALIDATE_RUNNER_UNLOCK (runner); + + return ret; +} + +static GList * +_do_report_synthesis (GstValidateRunner * runner) +{ + GHashTableIter iter; + GList *reports, *tmp; + gpointer key, value; + GList *criticals = NULL; + + g_hash_table_iter_init (&iter, runner->priv->reports_by_type); + while (g_hash_table_iter_next (&iter, &key, &value)) { + GstValidateReport *report; + reports = (GList *) value; + + if (!reports) + continue; + + report = (GstValidateReport *) (reports->data); + gst_validate_report_print_level (report); + gst_validate_report_print_detected_on (report); + + if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL) + criticals = g_list_append (criticals, report); + + for (tmp = g_list_next (reports); tmp; tmp = tmp->next) { + report = (GstValidateReport *) (tmp->data); + gst_validate_report_print_detected_on (report); + + if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL) + criticals = g_list_append (criticals, report); + } + report = (GstValidateReport *) (reports->data); + gst_validate_report_print_description (report); + gst_validate_printf (NULL, "\n"); + } + + return criticals; +} + +/** + * gst_validate_runner_printf: + * @runner: The #GstValidateRunner to print all the reports for + * + * Prints all the report on the terminal or on wherever set + * in the #GST_VALIDATE_FILE env variable. + * + * Returns: 0 if no critical error has been found and 18 if a critical + * error has been detected. That return value is usually to be used as + * exit code of the application. + */ +int +gst_validate_runner_printf (GstValidateRunner * runner) +{ + GList *reports, *tmp; + int ret = 0; + GList *criticals = NULL; + + criticals = _do_report_synthesis (runner); + reports = gst_validate_runner_get_reports (runner); + for (tmp = reports; tmp; tmp = tmp->next) { + GstValidateReport *report = tmp->data; + + if (gst_validate_report_should_print (report)) + gst_validate_report_printf (report); + + if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL) { + criticals = g_list_append (criticals, tmp->data); + } + } + + if (criticals) { + GList *iter; + + g_printerr ("\n\n==== Got criticals, Return value set to 18 ====\n"); + ret = 18; + + for (iter = criticals; iter; iter = iter->next) { + g_printerr (" Critical error %s\n", + ((GstValidateReport *) (iter->data))->message); + } + g_printerr ("\n"); + } + + g_list_free_full (reports, (GDestroyNotify) gst_validate_report_unref); + g_list_free (criticals); + gst_validate_printf (NULL, "Issues found: %u\n", + gst_validate_runner_get_reports_count (runner)); + return ret; +} + +int +gst_validate_runner_exit (GstValidateRunner * runner, gboolean print_result) +{ + gint ret = 0; + + g_signal_emit (runner, _signals[STOPPING_SIGNAL], 0); + + if (print_result) { + ret = gst_validate_runner_printf (runner); + } else { + GList *tmp; + + for (tmp = runner->priv->reports; tmp; tmp = tmp->next) { + GstValidateReport *report = tmp->data; + + if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL) + ret = 18; + } + } + + return ret; +} diff --git a/validate/gst/validate/gst-validate-runner.h b/validate/gst/validate/gst-validate-runner.h new file mode 100644 index 0000000..3c9b31d --- /dev/null +++ b/validate/gst/validate/gst-validate-runner.h @@ -0,0 +1,91 @@ +/* GStreamer + * Copyright (C) 2013 Thiago Santos + * + * gst-validate-runner.h - Validate Runner class + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_VALIDATE_RUNNER_H__ +#define __GST_VALIDATE_RUNNER_H__ + +#include +#include + +typedef struct _GstValidateRunner GstValidateRunner; +typedef struct _GstValidateRunnerClass GstValidateRunnerClass; + +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_VALIDATE_RUNNER (gst_validate_runner_get_type ()) +#define GST_IS_VALIDATE_RUNNER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_VALIDATE_RUNNER)) +#define GST_IS_VALIDATE_RUNNER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_VALIDATE_RUNNER)) +#define GST_VALIDATE_RUNNER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_VALIDATE_RUNNER, GstValidateRunnerClass)) +#define GST_VALIDATE_RUNNER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_VALIDATE_RUNNER, GstValidateRunner)) +#define GST_VALIDATE_RUNNER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_VALIDATE_RUNNER, GstValidateRunnerClass)) +#define GST_VALIDATE_RUNNER_CAST(obj) ((GstValidateRunner*)(obj)) +#define GST_VALIDATE_RUNNER_CLASS_CAST(klass) ((GstValidateRunnerClass*)(klass)) + +typedef struct _GstValidateRunnerPrivate GstValidateRunnerPrivate; + +/** + * GstValidateRunner: + * + * GStreamer Validate Runner class. + * + * Class that manages a Validate test run for some pipeline + */ +struct _GstValidateRunner { + GObject object; + + /* */ + GstValidateRunnerPrivate *priv; +}; + +/** + * GstValidateRunnerClass: + * @parent_class: parent + * + * GStreamer Validate Runner object class. + */ +struct _GstValidateRunnerClass { + GObjectClass parent_class; +}; + +/* normal GObject stuff */ +GType gst_validate_runner_get_type (void); + +GstValidateRunner * gst_validate_runner_new (void); + +void gst_validate_runner_add_report (GstValidateRunner * runner, GstValidateReport * report); + +guint gst_validate_runner_get_reports_count (GstValidateRunner * runner); +GList * gst_validate_runner_get_reports (GstValidateRunner * runner); + +int gst_validate_runner_printf (GstValidateRunner * runner); +int gst_validate_runner_exit (GstValidateRunner * runner, gboolean print_result); + +GstValidateReportingDetails gst_validate_runner_get_default_reporting_level (GstValidateRunner *runner); +GstValidateReportingDetails gst_validate_runner_get_reporting_level_for_name (GstValidateRunner *runner, + const gchar *name); + +G_END_DECLS + +#endif /* __GST_VALIDATE_RUNNER_H__ */ + diff --git a/validate/gst/validate/gst-validate-scenario.c b/validate/gst/validate/gst-validate-scenario.c new file mode 100644 index 0000000..bc4a06a --- /dev/null +++ b/validate/gst/validate/gst-validate-scenario.c @@ -0,0 +1,3511 @@ +/* GStreamer + * + * Copyright (C) 2013 Collabora Ltd. + * Author: Thibault Saunier + * + * gst-validate-scenario.c - Validate Scenario class + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/** + * SECTION:gst-validate-scenario + * @short_description: A GstValidateScenario represents a set of actions to be executed on a pipeline. + * + * A #GstValidateScenario represents the scenario that will be executed on a #GstPipeline. + * It is basically an ordered list of #GstValidateAction that will be executed during the + * execution of the pipeline. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +#include "gst-validate-internal.h" +#include "gst-validate-scenario.h" +#include "gst-validate-reporter.h" +#include "gst-validate-report.h" +#include "gst-validate-utils.h" +#include +#include + +#define GST_VALIDATE_SCENARIO_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), GST_TYPE_VALIDATE_SCENARIO, GstValidateScenarioPrivate)) + +#define GST_VALIDATE_SCENARIO_SUFFIX ".scenario" +#define GST_VALIDATE_SCENARIO_DIRECTORY "scenarios" + +#define DEFAULT_SEEK_TOLERANCE (1 * GST_MSECOND) /* tolerance seek interval + TODO make it overridable */ + +GST_DEBUG_CATEGORY_STATIC (gst_validate_scenario_debug); +#undef GST_CAT_DEFAULT +#define GST_CAT_DEFAULT gst_validate_scenario_debug + +#define REGISTER_ACTION_TYPE(_tname, _function, _params, _desc, _is_config) G_STMT_START { \ + gst_validate_register_action_type ((_tname), "core", (_function), (_params), (_desc), (_is_config)); \ +} G_STMT_END + +#define SCENARIO_LOCK(scenario) (g_mutex_lock(&scenario->priv->lock)) +#define SCENARIO_UNLOCK(scenario) (g_mutex_unlock(&scenario->priv->lock)) +enum +{ + PROP_0, + PROP_RUNNER, + PROP_HANDLES_STATE, + PROP_EXECUTE_ON_IDLE, + PROP_LAST +}; + +enum +{ + DONE, + LAST_SIGNAL +}; + +static guint scenario_signals[LAST_SIGNAL] = { 0 }; + +static GList *action_types = NULL; +static void gst_validate_scenario_dispose (GObject * object); +static void gst_validate_scenario_finalize (GObject * object); +static GstValidateActionType *_find_action_type (const gchar * type_name); + +static GPrivate main_thread_priv; + +/* GstValidateScenario is not really thread safe and + * everything should be done from the thread GstValidate + * was inited from, unless stated otherwize. + */ +struct _GstValidateScenarioPrivate +{ + GstBus *bus; + GstValidateRunner *runner; + gboolean execute_on_idle; + + GMutex lock; + + GList *actions; + GList *interlaced_actions; /* MT safe. Protected with SCENARIO_LOCK */ + GList *on_addition_actions; /* MT safe. Protected with SCENARIO_LOCK */ + + /* List of action that need parsing when reaching ASYNC_DONE + * most probably to be able to query duration */ + GList *needs_parsing; + + GstEvent *last_seek; + GstSeekFlags seek_flags; + GstClockTime segment_start; + GstClockTime segment_stop; + GstClockTime seek_pos_tol; + + /* If we seeked in paused the position should be exactly what + * the seek value was (if accurate) */ + gboolean seeked_in_pause; + + guint num_actions; + + gboolean handles_state; + + guint execute_actions_source_id; /* MT safe. Protect with SCENARIO_LOCK */ + guint wait_id; + guint signal_handler_id; + + /* Name of message the wait action is waiting for */ + const gchar *message_type; + + gboolean buffering; + + gboolean got_eos; + gboolean changing_state; + GstState target_state; + + GList *overrides; + + GstStructure *description; +}; + +typedef struct KeyFileGroupName +{ + GKeyFile *kf; + gchar *group_name; +} KeyFileGroupName; + +static GstValidateInterceptionReturn +gst_validate_scenario_intercept_report (GstValidateReporter * reporter, + GstValidateReport * report) +{ + GList *tmp; + + for (tmp = GST_VALIDATE_SCENARIO (reporter)->priv->overrides; tmp; + tmp = tmp->next) { + report->level = + gst_validate_override_get_severity (tmp->data, + gst_validate_issue_get_id (report->issue), report->level); + } + + return GST_VALIDATE_REPORTER_REPORT; +} + +static void +_reporter_iface_init (GstValidateReporterInterface * iface) +{ + iface->intercept_report = gst_validate_scenario_intercept_report; +} + +G_DEFINE_TYPE_WITH_CODE (GstValidateScenario, gst_validate_scenario, + G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (GST_TYPE_VALIDATE_REPORTER, + _reporter_iface_init)); + +/* GstValidateAction implementation */ +GType _gst_validate_action_type; + +struct _GstValidateActionPrivate +{ + GstStructure *main_structure; + GstValidateExecuteActionReturn state; /* Actually ActionState */ + gboolean printed; + gboolean executing_last_subaction; + gboolean optional; +}; + +GST_DEFINE_MINI_OBJECT_TYPE (GstValidateAction, gst_validate_action); +static GstValidateAction *gst_validate_action_new (GstValidateScenario * + scenario, GstValidateActionType * type); +static gboolean execute_next_action (GstValidateScenario * scenario); + +static GstValidateAction * +_action_copy (GstValidateAction * act) +{ + GstValidateAction *copy = gst_validate_action_new (act->scenario, + _find_action_type (act->type)); + + if (act->structure) { + copy->structure = gst_structure_copy (act->structure); + copy->type = gst_structure_get_name (copy->structure); + if (!(act->name = gst_structure_get_string (copy->structure, "name"))) + act->name = ""; + } + + if (act->priv->main_structure) + copy->priv->main_structure = gst_structure_copy (act->priv->main_structure); + + copy->action_number = act->action_number; + copy->playback_time = act->playback_time; + + return copy; +} + +static void +_action_free (GstValidateAction * action) +{ + if (action->structure) + gst_structure_free (action->structure); + + if (action->priv->main_structure) + gst_structure_free (action->priv->main_structure); + + if (action->scenario) + g_object_remove_weak_pointer (G_OBJECT (action->scenario), + ((gpointer *) & action->scenario)); + + g_slice_free (GstValidateActionPrivate, action->priv); + g_slice_free (GstValidateAction, action); +} + +static void +gst_validate_action_init (GstValidateAction * action) +{ + gst_mini_object_init (((GstMiniObject *) action), 0, + _gst_validate_action_type, (GstMiniObjectCopyFunction) _action_copy, NULL, + (GstMiniObjectFreeFunction) _action_free); + + action->priv = g_slice_new0 (GstValidateActionPrivate); +} + +static void +gst_validate_action_unref (GstValidateAction * action) +{ + gst_mini_object_unref (GST_MINI_OBJECT (action)); +} + +static GstValidateAction * +gst_validate_action_new (GstValidateScenario * scenario, + GstValidateActionType * action_type) +{ + GstValidateAction *action = g_slice_new0 (GstValidateAction); + + gst_validate_action_init (action); + action->playback_time = GST_CLOCK_TIME_NONE; + action->type = action_type->name; + action->repeat = -1; + + action->scenario = scenario; + if (scenario) + g_object_add_weak_pointer (G_OBJECT (scenario), + ((gpointer *) & action->scenario)); + + return action; +} + +gboolean +_action_check_and_set_printed (GstValidateAction * action) +{ + if (action->priv->printed == FALSE) { + action->priv->printed = TRUE; + + return FALSE; + } + + return TRUE; +} + +gboolean +gst_validate_action_is_subaction (GstValidateAction * action) +{ + return !gst_structure_is_equal (action->structure, + action->priv->main_structure); +} + +/* GstValidateActionType implementation */ +GType _gst_validate_action_type_type; +GST_DEFINE_MINI_OBJECT_TYPE (GstValidateActionType, gst_validate_action_type); +static GstValidateActionType *gst_validate_action_type_new (void); + +static void +_action_type_free (GstValidateActionType * type) +{ + g_free (type->parameters); + g_free (type->description); + g_free (type->name); + g_free (type->implementer_namespace); + + if (type->overriden_type) + gst_mini_object_unref (GST_MINI_OBJECT (type->overriden_type)); +} + +static void +gst_validate_action_type_init (GstValidateActionType * type) +{ + gst_mini_object_init ((GstMiniObject *) type, 0, + _gst_validate_action_type_type, NULL, NULL, + (GstMiniObjectFreeFunction) _action_type_free); +} + +GstValidateActionType * +gst_validate_action_type_new (void) +{ + GstValidateActionType *type = g_slice_new0 (GstValidateActionType); + + gst_validate_action_type_init (type); + + return type; +} + +static GstValidateActionType * +_find_action_type (const gchar * type_name) +{ + GList *tmp; + + for (tmp = action_types; tmp; tmp = tmp->next) { + if (g_strcmp0 (((GstValidateActionType *) tmp->data)->name, type_name) == 0) + return tmp->data; + } + + return NULL; +} + +static gboolean +_set_variable_func (const gchar * name, double *value, gpointer user_data) +{ + GstValidateScenario *scenario = GST_VALIDATE_SCENARIO (user_data); + + if (!g_strcmp0 (name, "duration")) { + gint64 duration; + + if (!gst_element_query_duration (scenario->pipeline, + GST_FORMAT_TIME, &duration)) { + GST_WARNING_OBJECT (scenario, "Could not query duration"); + return FALSE; + } + + if (!GST_CLOCK_TIME_IS_VALID (duration)) + *value = G_MAXDOUBLE; + else + *value = ((double) duration / GST_SECOND); + + return TRUE; + } else if (!g_strcmp0 (name, "position")) { + gint64 position; + + if (!gst_element_query_position (scenario->pipeline, + GST_FORMAT_TIME, &position)) { + GST_WARNING_OBJECT (scenario, "Could not query position"); + return FALSE; + } + + if (!GST_CLOCK_TIME_IS_VALID (position)) + *value = G_MAXDOUBLE; + else + *value = ((double) position / GST_SECOND); + + + return TRUE; + } + + return FALSE; +} + +/* Check that @list doesn't contain any non-optional actions */ +static gboolean +actions_list_is_done (GList * list) +{ + GList *l; + + for (l = list; l != NULL; l = g_list_next (l)) { + GstValidateAction *action = l->data; + + if (!action->priv->optional) + return FALSE; + } + + return TRUE; +} + +static void +_check_scenario_is_done (GstValidateScenario * scenario) +{ + SCENARIO_LOCK (scenario); + if (actions_list_is_done (scenario->priv->actions) && + actions_list_is_done (scenario->priv->interlaced_actions) && + actions_list_is_done (scenario->priv->on_addition_actions)) { + SCENARIO_UNLOCK (scenario); + + g_signal_emit (scenario, scenario_signals[DONE], 0); + } else { + SCENARIO_UNLOCK (scenario); + } +} + +/** + * gst_validate_action_get_clocktime: + * @scenario: The #GstValidateScenario from which to get a time + * for a parameter of an action + * @action: The action from which to retrieve the time for @name + * parameter. + * @name: The name of the parameter for which to retrive a time + * @retval: (out): The return value for the wanted time + * + * + * Get a time value for the @name parameter of an action. This + * method should be called to retrived and compute a timed value of a given + * action. It will first try to retrieve the value as a double, + * then get it as a string and execute any formula taking into account + * the 'position' and 'duration' variables. And it will always convert that + * value to a GstClockTime. + * + * Returns: %TRUE if the time value could be retrieved/computed or %FALSE otherwize + */ +gboolean +gst_validate_action_get_clocktime (GstValidateScenario * scenario, + GstValidateAction * action, const gchar * name, GstClockTime * retval) +{ + if (!gst_validate_utils_get_clocktime (action->structure, name, retval)) { + gdouble val; + gchar *error = NULL; + const gchar *strval; + + if (!(strval = gst_structure_get_string (action->structure, name))) { + GST_INFO_OBJECT (scenario, "Could not find %s", name); + return -1; + } + + val = gst_validate_utils_parse_expression (strval, _set_variable_func, + scenario, &error); + + if (error) { + GST_WARNING ("Error while parsing %s: %s", strval, error); + g_free (error); + + return FALSE; + } else if (val == -1.0) { + *retval = GST_CLOCK_TIME_NONE; + } else { + *retval = val * GST_SECOND; + *retval = GST_ROUND_UP_4 (*retval); + } + + return TRUE; + } + + return TRUE; +} + +/** + * gst_validate_scenario_execute_seek: + * @scenario: The #GstValidateScenario for which to execute a seek action + * @action: The seek action to execute + * @rate: The playback rate of the seek + * @format: The #GstFormat of the seek + * @flags: The #GstSeekFlags of the seek + * @start_type: The #GstSeekType of the start value of the seek + * @start: The start time of the seek + * @stop_type: The #GstSeekType of the stop value of the seek + * @stop: The stop time of the seek + * + * Executes a seek event on the scenario' pipeline. You should always use + * that method when you want to execute a seek inside a new action types + * so that the scenario state is updated taking into account that seek. + * + * For more information you should have a look at #gst_event_new_seek + * + * Returns: %TRUE if the seek could be executed, %FALSE otherwize + */ +gboolean +gst_validate_scenario_execute_seek (GstValidateScenario * scenario, + GstValidateAction * action, gdouble rate, GstFormat format, + GstSeekFlags flags, GstSeekType start_type, GstClockTime start, + GstSeekType stop_type, GstClockTime stop) +{ + GstValidateExecuteActionReturn ret = GST_VALIDATE_EXECUTE_ACTION_ASYNC; + GstValidateScenarioPrivate *priv = scenario->priv; + + GstEvent *seek = gst_event_new_seek (rate, format, flags, start_type, start, + stop_type, stop); + + gst_event_ref (seek); + if (gst_element_send_event (scenario->pipeline, seek)) { + gst_event_replace (&priv->last_seek, seek); + priv->seek_flags = flags; + } else { + GST_VALIDATE_REPORT (scenario, EVENT_SEEK_NOT_HANDLED, + "Could not execute seek: '(position %" GST_TIME_FORMAT + "), %s (num %u, missing repeat: %i), seeking to: %" GST_TIME_FORMAT + " stop: %" GST_TIME_FORMAT " Rate %lf'", + GST_TIME_ARGS (action->playback_time), action->name, + action->action_number, action->repeat, GST_TIME_ARGS (start), + GST_TIME_ARGS (stop), rate); + ret = GST_VALIDATE_EXECUTE_ACTION_ERROR; + } + gst_event_unref (seek); + + return ret; +} + +static gint +_execute_seek (GstValidateScenario * scenario, GstValidateAction * action) +{ + const char *str_format, *str_flags, *str_start_type, *str_stop_type; + + gdouble rate = 1.0; + GstFormat format = GST_FORMAT_TIME; + GstSeekFlags flags = 0; + GstSeekType start_type = GST_SEEK_TYPE_SET; + GstClockTime start; + GstSeekType stop_type = GST_SEEK_TYPE_SET; + GstClockTime stop = GST_CLOCK_TIME_NONE; + + if (!gst_validate_action_get_clocktime (scenario, action, "start", &start)) + return GST_VALIDATE_EXECUTE_ACTION_ERROR; + + gst_structure_get_double (action->structure, "rate", &rate); + if ((str_format = gst_structure_get_string (action->structure, "format"))) + gst_validate_utils_enum_from_str (GST_TYPE_FORMAT, str_format, &format); + + if ((str_start_type = + gst_structure_get_string (action->structure, "start_type"))) + gst_validate_utils_enum_from_str (GST_TYPE_SEEK_TYPE, str_start_type, + &start_type); + + if ((str_stop_type = + gst_structure_get_string (action->structure, "stop_type"))) + gst_validate_utils_enum_from_str (GST_TYPE_SEEK_TYPE, str_stop_type, + &stop_type); + + if ((str_flags = gst_structure_get_string (action->structure, "flags"))) + flags = gst_validate_utils_flags_from_str (GST_TYPE_SEEK_FLAGS, str_flags); + + gst_validate_action_get_clocktime (scenario, action, "stop", &stop); + + return gst_validate_scenario_execute_seek (scenario, action, rate, format, + flags, start_type, start, stop_type, stop); +} + +static gboolean +_pause_action_restore_playing (GstValidateScenario * scenario) +{ + GstElement *pipeline = scenario->pipeline; + + + gst_validate_printf (scenario, "Back to playing\n"); + + if (gst_element_set_state (pipeline, GST_STATE_PLAYING) == + GST_STATE_CHANGE_FAILURE) { + GST_VALIDATE_REPORT (scenario, STATE_CHANGE_FAILURE, + "Failed to set state to playing"); + } + + return FALSE; +} + +static GstValidateExecuteActionReturn +_execute_set_state (GstValidateScenario * scenario, GstValidateAction * action) +{ + GstState state; + const gchar *str_state; + + GstStateChangeReturn ret; + + g_return_val_if_fail ((str_state = + gst_structure_get_string (action->structure, "state")), FALSE); + + g_return_val_if_fail (gst_validate_utils_enum_from_str (GST_TYPE_STATE, + str_state, &state), FALSE); + + scenario->priv->target_state = state; + scenario->priv->changing_state = TRUE; + scenario->priv->seeked_in_pause = FALSE; + + ret = gst_element_set_state (scenario->pipeline, state); + + if (ret == GST_STATE_CHANGE_FAILURE) { + scenario->priv->changing_state = FALSE; + GST_VALIDATE_REPORT (scenario, STATE_CHANGE_FAILURE, + "Failed to set state to %s", str_state); + + /* Nothing async on failure, action will be removed automatically */ + return GST_VALIDATE_EXECUTE_ACTION_ERROR; + } else if (ret == GST_STATE_CHANGE_ASYNC) { + + return GST_VALIDATE_EXECUTE_ACTION_ASYNC; + } + + scenario->priv->changing_state = FALSE; + + return GST_VALIDATE_EXECUTE_ACTION_OK; +} + +static gboolean +_execute_pause (GstValidateScenario * scenario, GstValidateAction * action) +{ + GstClockTime duration = 0; + GstStateChangeReturn ret; + + gst_structure_get (action->structure, "duration", G_TYPE_UINT64, &duration, + NULL); + gst_structure_set (action->structure, "state", G_TYPE_STRING, "paused", NULL); + + GST_INFO_OBJECT (scenario, "Pausing for %" GST_TIME_FORMAT, + GST_TIME_ARGS (duration)); + + ret = _execute_set_state (scenario, action); + + if (ret && duration) + g_timeout_add (GST_TIME_AS_MSECONDS (duration), + (GSourceFunc) _pause_action_restore_playing, scenario); + + return ret; +} + +static gboolean +_execute_play (GstValidateScenario * scenario, GstValidateAction * action) +{ + GST_DEBUG ("Playing back"); + + gst_structure_set (action->structure, "state", G_TYPE_STRING, + "playing", NULL); + + + return _execute_set_state (scenario, action); +} + +static gboolean +_action_sets_state (GstValidateAction * action) +{ + if (action == NULL) + return FALSE; + + if (g_strcmp0 (action->type, "set-state") == 0) + return TRUE; + + if (g_strcmp0 (action->type, "play") == 0) + return TRUE; + + if (g_strcmp0 (action->type, "pause") == 0) + return TRUE; + + return FALSE; + +} + +static gboolean +_execute_stop (GstValidateScenario * scenario, GstValidateAction * action) +{ + GstValidateScenarioPrivate *priv = scenario->priv; + GstBus *bus = gst_element_get_bus (scenario->pipeline); + + SCENARIO_LOCK (scenario); + if (priv->execute_actions_source_id) { + g_source_remove (priv->execute_actions_source_id); + priv->execute_actions_source_id = 0; + } + SCENARIO_UNLOCK (scenario); + + gst_bus_post (bus, + gst_message_new_request_state (GST_OBJECT_CAST (scenario), + GST_STATE_NULL)); + gst_object_unref (bus); + + return TRUE; +} + +static gboolean +_execute_eos (GstValidateScenario * scenario, GstValidateAction * action) +{ + GST_DEBUG ("Sending eos to pipeline at %" GST_TIME_FORMAT, + GST_TIME_ARGS (action->playback_time)); + + return gst_element_send_event (scenario->pipeline, gst_event_new_eos ()); +} + +static int +find_input_selector (GValue * velement, const gchar * type) +{ + GstElement *element = g_value_get_object (velement); + int result = !0; + + if (G_OBJECT_TYPE (element) == g_type_from_name ("GstInputSelector")) { + GstPad *srcpad = gst_element_get_static_pad (element, "src"); + + if (srcpad) { + GstCaps *caps = gst_pad_query_caps (srcpad, NULL); + + if (caps) { + const char *mime = + gst_structure_get_name (gst_caps_get_structure (caps, 0)); + gboolean found = FALSE; + + if (g_strcmp0 (type, "audio") == 0) + found = g_str_has_prefix (mime, "audio/"); + else if (g_strcmp0 (type, "video") == 0) + found = g_str_has_prefix (mime, "video/") + && !g_str_has_prefix (mime, "video/x-dvd-subpicture"); + else if (g_strcmp0 (type, "text") == 0) + found = g_str_has_prefix (mime, "text/") + || g_str_has_prefix (mime, "subtitle/") + || g_str_has_prefix (mime, "video/x-dvd-subpicture"); + + gst_object_unref (srcpad); + if (found) + result = 0; + } + + gst_caps_unref (caps); + } + } + return result; +} + +static GstElement * +find_input_selector_with_type (GstBin * bin, const gchar * type) +{ + GValue result = { 0, }; + GstElement *input_selector = NULL; + GstIterator *iterator = gst_bin_iterate_recurse (bin); + + if (gst_iterator_find_custom (iterator, + (GCompareFunc) find_input_selector, &result, (gpointer) type)) { + input_selector = g_value_get_object (&result); + } + gst_iterator_free (iterator); + + return input_selector; +} + +static GstPad * +find_nth_sink_pad (GstElement * element, int index) +{ + GstIterator *iterator; + gboolean done = FALSE; + GstPad *pad = NULL; + int dec_index = index; + GValue data = { 0, }; + + iterator = gst_element_iterate_sink_pads (element); + while (!done) { + switch (gst_iterator_next (iterator, &data)) { + case GST_ITERATOR_OK: + if (!dec_index--) { + done = TRUE; + pad = g_value_get_object (&data); + break; + } + g_value_reset (&data); + break; + case GST_ITERATOR_RESYNC: + gst_iterator_resync (iterator); + dec_index = index; + break; + case GST_ITERATOR_ERROR: + done = TRUE; + break; + case GST_ITERATOR_DONE: + done = TRUE; + break; + } + } + gst_iterator_free (iterator); + return pad; +} + +static int +find_sink_pad_index (GstElement * element, GstPad * pad) +{ + GstIterator *iterator; + gboolean done = FALSE; + int index = 0; + GValue data = { 0, }; + + iterator = gst_element_iterate_sink_pads (element); + while (!done) { + switch (gst_iterator_next (iterator, &data)) { + case GST_ITERATOR_OK: + if (pad == g_value_get_object (&data)) { + done = TRUE; + } else { + index++; + } + g_value_reset (&data); + break; + case GST_ITERATOR_RESYNC: + gst_iterator_resync (iterator); + index = 0; + break; + case GST_ITERATOR_ERROR: + done = TRUE; + break; + case GST_ITERATOR_DONE: + done = TRUE; + break; + } + } + gst_iterator_free (iterator); + return index; +} + +static GstPadProbeReturn +_check_select_pad_done (GstPad * pad, GstPadProbeInfo * info, + GstValidateAction * action) +{ + if (GST_BUFFER_FLAG_IS_SET (info->data, GST_BUFFER_FLAG_DISCONT)) { + gst_validate_action_set_done (action); + + return GST_PAD_PROBE_REMOVE; + } + + return GST_PAD_PROBE_OK; +} + +static gboolean +_execute_switch_track (GstValidateScenario * scenario, + GstValidateAction * action) +{ + guint index; + gboolean relative = FALSE; + const gchar *type, *str_index; + GstElement *input_selector; + + if (!(type = gst_structure_get_string (action->structure, "type"))) + type = "audio"; + + /* First find an input selector that has the right type */ + input_selector = + find_input_selector_with_type (GST_BIN (scenario->pipeline), type); + if (input_selector) { + GstState state, next; + GstPad *pad, *cpad, *srcpad; + + GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK; + + if ((str_index = gst_structure_get_string (action->structure, "index"))) { + if (!gst_structure_get_uint (action->structure, "index", &index)) { + GST_WARNING ("No index given, defaulting to +1"); + index = 1; + relative = TRUE; + } + } else { + relative = strchr ("+-", str_index[0]) != NULL; + index = g_ascii_strtoll (str_index, NULL, 10); + } + + if (relative) { /* We are changing track relatively to current track */ + int npads; + + g_object_get (input_selector, "active-pad", &pad, "n-pads", &npads, NULL); + if (pad) { + int current_index = find_sink_pad_index (input_selector, pad); + + index = (current_index + index) % npads; + gst_object_unref (pad); + } + } + + pad = find_nth_sink_pad (input_selector, index); + g_object_get (input_selector, "active-pad", &cpad, NULL); + if (gst_element_get_state (scenario->pipeline, &state, &next, 0) && + state == GST_STATE_PLAYING && next == GST_STATE_VOID_PENDING) { + srcpad = gst_element_get_static_pad (input_selector, "src"); + + gst_pad_add_probe (srcpad, + GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BUFFER_LIST, + (GstPadProbeCallback) _check_select_pad_done, action, NULL); + res = GST_VALIDATE_EXECUTE_ACTION_ASYNC; + gst_object_unref (srcpad); + } + + g_object_set (input_selector, "active-pad", pad, NULL); + gst_object_unref (pad); + gst_object_unref (cpad); + gst_object_unref (input_selector); + + return res; + } + + /* No selector found -> Failed */ + return GST_VALIDATE_EXECUTE_ACTION_ERROR; +} + +static gboolean +_set_rank (GstValidateScenario * scenario, GstValidateAction * action) +{ + guint rank; + GstPluginFeature *feature; + const gchar *feature_name; + + if (!(feature_name = + gst_structure_get_string (action->structure, "feature-name"))) { + GST_ERROR ("Could not find the name of the feature to tweak"); + + return FALSE; + } + + if (!(gst_structure_get_uint (action->structure, "rank", &rank) || + gst_structure_get_int (action->structure, "rank", (gint *) & rank))) { + GST_ERROR ("Could not get rank to set on %s", feature_name); + + return FALSE; + } + + feature = gst_registry_lookup_feature (gst_registry_get (), feature_name); + if (!feature) { + GST_ERROR ("Could not find feaure %s", feature_name); + + return FALSE; + } + + gst_plugin_feature_set_rank (feature, rank); + gst_object_unref (feature); + + return TRUE; +} + +static inline gboolean +_add_execute_actions_gsource (GstValidateScenario * scenario) +{ + GstValidateScenarioPrivate *priv = scenario->priv; + + SCENARIO_LOCK (scenario); + if (priv->execute_actions_source_id == 0 && priv->wait_id == 0 + && priv->signal_handler_id == 0 && priv->message_type == NULL) { + priv->execute_actions_source_id = + g_idle_add ((GSourceFunc) execute_next_action, scenario); + SCENARIO_UNLOCK (scenario); + + GST_DEBUG_OBJECT (scenario, "Start checking position again"); + return TRUE; + } + SCENARIO_UNLOCK (scenario); + + GST_LOG_OBJECT (scenario, "No need to start a new gsource"); + return FALSE; +} + +static gboolean +_get_position (GstValidateScenario * scenario, + GstValidateAction * act, GstClockTime * position) +{ + gboolean has_pos = FALSE, has_dur = FALSE; + GstClockTime duration = -1; + + GstValidateScenarioPrivate *priv = scenario->priv; + GstElement *pipeline = scenario->pipeline; + + has_pos = gst_element_query_position (pipeline, GST_FORMAT_TIME, + (gint64 *) position) + && GST_CLOCK_TIME_IS_VALID (*position); + has_dur = + gst_element_query_duration (pipeline, GST_FORMAT_TIME, + (gint64 *) & duration) + && GST_CLOCK_TIME_IS_VALID (duration); + + if (!has_pos && GST_STATE (pipeline) >= GST_STATE_PAUSED && + act && GST_CLOCK_TIME_IS_VALID (act->playback_time)) { + GST_INFO_OBJECT (scenario, "Unknown position: %" GST_TIME_FORMAT, + GST_TIME_ARGS (*position)); + + return FALSE; + } + + if (has_pos && has_dur && !priv->got_eos) { + if (*position > duration) { + _add_execute_actions_gsource (scenario); + + GST_VALIDATE_REPORT (scenario, + QUERY_POSITION_SUPERIOR_DURATION, + "Reported position %" GST_TIME_FORMAT " > reported duration %" + GST_TIME_FORMAT, GST_TIME_ARGS (*position), GST_TIME_ARGS (duration)); + + return TRUE; + } + } + + return TRUE; +} + +static gboolean +_check_position (GstValidateScenario * scenario, GstValidateAction * act, + GstClockTime * position) +{ + GstQuery *query; + gdouble rate; + + GstClockTime start_with_tolerance, stop_with_tolerance; + GstValidateScenarioPrivate *priv = scenario->priv; + + if (scenario->pipeline == NULL) { + GST_INFO_OBJECT (scenario, "No pipeline set anymore"); + + return TRUE; + } + + if (!_get_position (scenario, act, position)) + return FALSE; + + GST_DEBUG_OBJECT (scenario, "Current position: %" GST_TIME_FORMAT, + GST_TIME_ARGS (*position)); + + /* Check if playback is within seek segment */ + start_with_tolerance = + MAX (0, (gint64) (priv->segment_start - priv->seek_pos_tol)); + stop_with_tolerance = + GST_CLOCK_TIME_IS_VALID (priv->segment_stop) ? priv->segment_stop + + priv->seek_pos_tol : -1; + + if ((GST_CLOCK_TIME_IS_VALID (stop_with_tolerance) + && *position > stop_with_tolerance) + || (priv->seek_flags & GST_SEEK_FLAG_ACCURATE + && *position < start_with_tolerance)) { + + GST_VALIDATE_REPORT (scenario, QUERY_POSITION_OUT_OF_SEGMENT, + "Current position %" GST_TIME_FORMAT " not in the expected range [%" + GST_TIME_FORMAT " -- %" GST_TIME_FORMAT, GST_TIME_ARGS (*position), + GST_TIME_ARGS (start_with_tolerance), + GST_TIME_ARGS (stop_with_tolerance)); + } + + query = gst_query_new_segment (GST_FORMAT_DEFAULT); + if (gst_element_query (GST_ELEMENT (scenario->pipeline), query)) + gst_query_parse_segment (query, &rate, NULL, NULL, NULL); + gst_query_unref (query); + + if (priv->seeked_in_pause && priv->seek_flags & GST_SEEK_FLAG_ACCURATE) { + if ((rate > 0 && (*position >= priv->segment_start + priv->seek_pos_tol || + *position < MIN (0, + ((gint64) priv->segment_start - priv->seek_pos_tol)))) + || (rate < 0 && (*position > priv->segment_start + priv->seek_pos_tol + || *position < MIN (0, + (gint64) priv->segment_start - priv->seek_pos_tol)))) { + priv->seeked_in_pause = FALSE; + GST_VALIDATE_REPORT (scenario, EVENT_SEEK_RESULT_POSITION_WRONG, + "Reported position after accurate seek in PAUSED state should be exactlty" + " what the user asked for %" GST_TIME_FORMAT " != %" GST_TIME_FORMAT, + GST_TIME_ARGS (*position), GST_TIME_ARGS (priv->segment_start)); + } + } + + return TRUE; +} + +static gboolean +_should_execute_action (GstValidateScenario * scenario, GstValidateAction * act, + GstClockTime position, gdouble rate) +{ + + if (!act) { + GST_DEBUG_OBJECT (scenario, "No action to execute"); + + return FALSE; + } else if (scenario->pipeline == NULL) { + + if (!(GST_VALIDATE_ACTION_GET_TYPE (act)->flags & + GST_VALIDATE_ACTION_TYPE_DOESNT_NEED_PIPELINE)) { + GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR, + "Trying to execute an %s action after the pipeline has been destroyed" + " but the type has not been marked as " + "GST_VALIDATE_ACTION_TYPE_DOESNT_NEED_PIPELINE", act->type); + + return FALSE; + } else if (GST_CLOCK_TIME_IS_VALID (act->playback_time)) { + GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR, + "Trying to execute action %s with playback time %" GST_TIME_FORMAT + " after the pipeline has been destroyed. It is impossible" + " to execute an action with a playback time specified " + " after the pipeline has been destroyed", + act->type, GST_TIME_ARGS (act->playback_time)); + + return FALSE; + } + + GST_DEBUG_OBJECT (scenario, "No pipeline, go and execute action!"); + + return TRUE; + } else if (scenario->priv->got_eos) { + GST_DEBUG_OBJECT (scenario, "Just got EOS go and execute next action!"); + scenario->priv->got_eos = FALSE; + } else if (GST_STATE (scenario->pipeline) < GST_STATE_PAUSED) { + GST_DEBUG_OBJECT (scenario, "Pipeline not even in paused, " + "just executing actions"); + + return TRUE; + } else if (act->playback_time == GST_CLOCK_TIME_NONE) { + GST_DEBUG_OBJECT (scenario, "No timing info, executing action"); + + return TRUE; + } else if ((rate > 0 && (GstClockTime) position < act->playback_time)) { + GST_DEBUG_OBJECT (scenario, "positive rate and position %" GST_TIME_FORMAT + " < playback_time %" GST_TIME_FORMAT, GST_TIME_ARGS (position), + GST_TIME_ARGS (act->playback_time)); + + return FALSE; + } else if (rate < 0 && (GstClockTime) position > act->playback_time) { + GST_DEBUG_OBJECT (scenario, "negativ rate and position %" GST_TIME_FORMAT + " < playback_time %" GST_TIME_FORMAT, GST_TIME_ARGS (position), + GST_TIME_ARGS (act->playback_time)); + + return FALSE; + } + + return TRUE; +} + +GstValidateExecuteActionReturn +gst_validate_execute_action (GstValidateActionType * action_type, + GstValidateAction * action) +{ + GstValidateExecuteActionReturn res; + + g_return_val_if_fail (g_strcmp0 (action_type->name, action->type) == 0, + GST_VALIDATE_EXECUTE_ACTION_ERROR); + + if (action_type->prepare) { + if (action_type->prepare (action) == FALSE) { + GST_ERROR_OBJECT (action->scenario, "Action %" GST_PTR_FORMAT + " could not be prepared", action->structure); + + return GST_VALIDATE_EXECUTE_ACTION_ERROR; + } + } + + gst_validate_print_action (action, NULL); + + res = action_type->execute (action->scenario, action); + + if (!gst_structure_has_field (action->structure, "sub-action")) { + gst_structure_free (action->structure); + + action->priv->printed = FALSE; + action->structure = gst_structure_copy (action->priv->main_structure); + + if (res == GST_VALIDATE_EXECUTE_ACTION_ASYNC) + action->priv->executing_last_subaction = TRUE; + } + + return res; +} + +static gboolean +_set_action_playback_time (GstValidateScenario * scenario, + GstValidateAction * action) +{ + if (!gst_validate_action_get_clocktime (scenario, action, + "playback-time", &action->playback_time)) { + gchar *str = gst_structure_to_string (action->structure); + + g_error ("Could not parse playback-time on structure: %s", str); + g_free (str); + + return FALSE; + } + + gst_structure_set (action->structure, "playback-time", GST_TYPE_CLOCK_TIME, + action->playback_time, NULL); + + return TRUE; +} + +static GstValidateExecuteActionReturn +_fill_action (GstValidateScenario * scenario, GstValidateAction * action, + GstStructure * structure, gboolean add_to_lists) +{ + gdouble playback_time; + gboolean is_config = FALSE; + GstValidateActionType *action_type; + const gchar *str_playback_time = NULL; + GstValidateScenarioPrivate *priv = scenario->priv; + GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK; + gboolean optional; + + action->type = gst_structure_get_name (structure); + action_type = _find_action_type (action->type); + + if (!action_type) { + GST_ERROR_OBJECT (scenario, "Action type %s no found", + gst_structure_get_name (structure)); + + return GST_VALIDATE_EXECUTE_ACTION_ERROR; + } + + if (gst_structure_get_double (structure, "playback-time", &playback_time) || + gst_structure_get_double (structure, "playback_time", &playback_time)) { + action->playback_time = playback_time * GST_SECOND; + } else if ((str_playback_time = + gst_structure_get_string (structure, "playback-time")) || + (str_playback_time = + gst_structure_get_string (structure, "playback_time"))) { + + if (add_to_lists) + priv->needs_parsing = g_list_append (priv->needs_parsing, action); + else if (!_set_action_playback_time (scenario, action)) + return GST_VALIDATE_EXECUTE_ACTION_ERROR; + + } else + GST_INFO_OBJECT (scenario, + "No playback time for action %" GST_PTR_FORMAT, structure); + + if (!(action->name = gst_structure_get_string (structure, "name"))) + action->name = ""; + + action->structure = gst_structure_copy (structure); + + if (!action->priv->main_structure) + action->priv->main_structure = gst_structure_copy (structure); + + if (gst_structure_get_boolean (structure, "optional", &optional)) { + if ((action_type->flags & GST_VALIDATE_ACTION_TYPE_CAN_BE_OPTIONAL) == 0) { + GST_ERROR_OBJECT (scenario, "Action type %s can't be optional", + gst_structure_get_name (structure)); + return GST_VALIDATE_EXECUTE_ACTION_ERROR; + } + action->priv->optional = optional; + } + + if (IS_CONFIG_ACTION_TYPE (action_type->flags) || + (gst_structure_get_boolean (action->structure, "as-config", + &is_config) && is_config == TRUE)) { + + gst_validate_print_action (action, NULL); + res = action_type->execute (scenario, action); + + return res; + } + + if (!add_to_lists) + return res; + + if (str_playback_time == NULL) { + GstValidateActionType *type = _find_action_type (action->type); + + if (type->flags & GST_VALIDATE_ACTION_TYPE_CAN_EXECUTE_ON_ADDITION + && !GST_CLOCK_TIME_IS_VALID (action->playback_time)) { + SCENARIO_LOCK (scenario); + priv->on_addition_actions = g_list_append (priv->on_addition_actions, + action); + SCENARIO_UNLOCK (scenario); + + } else { + priv->actions = g_list_append (priv->actions, action); + } + } + + return res; +} + +static GstValidateExecuteActionReturn +_execute_sub_action_action (GstValidateAction * action) +{ + const gchar *subaction_str; + GstStructure *subaction_struct = NULL; + GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK; + + if (action->priv->executing_last_subaction) { + action->priv->executing_last_subaction = FALSE; + + goto done; + } + + subaction_str = gst_structure_get_string (action->structure, "sub-action"); + if (subaction_str) { + subaction_struct = gst_structure_from_string (subaction_str, NULL); + + if (subaction_struct == NULL) { + GST_VALIDATE_REPORT (action->scenario, SCENARIO_FILE_MALFORMED, + "Sub action %s could not be parsed", subaction_str); + + res = GST_VALIDATE_EXECUTE_ACTION_ERROR; + goto done; + } + + } else { + gst_structure_get (action->structure, "sub-action", GST_TYPE_STRUCTURE, + &subaction_struct, NULL); + } + + if (subaction_struct) { + if (action->structure) { + GST_INFO_OBJECT (action->scenario, "Clearing old action structure"); + gst_structure_free (action->structure); + } + + res = _fill_action (action->scenario, action, subaction_struct, FALSE); + if (res == GST_VALIDATE_EXECUTE_ACTION_ERROR) { + GST_VALIDATE_REPORT (action->scenario, SCENARIO_ACTION_EXECUTION_ERROR, + "Sub action %" GST_PTR_FORMAT " could not be filled", + subaction_struct); + + goto done; + } + + if (!GST_CLOCK_TIME_IS_VALID (action->playback_time)) { + GstValidateActionType *action_type = _find_action_type (action->type); + + action->priv->printed = FALSE; + res = gst_validate_execute_action (action_type, action); + + goto done; + } + + } + +done: + if (subaction_struct) + gst_structure_free (subaction_struct); + return res; +} + + +/* This is the main action execution function + * it checks whether it is time to run the next action + * and if it is the case executes it. + * + * If the 'execute-on-idle' property is not TRUE, + * the function will recurse while the actions are run + * synchronously + */ +static gboolean +execute_next_action (GstValidateScenario * scenario) +{ + GList *tmp; + gdouble rate = 1.0; + GstClockTime position = -1; + GstValidateAction *act = NULL; + GstValidateActionType *type; + + GstValidateScenarioPrivate *priv = scenario->priv; + + if (priv->buffering) { + GST_DEBUG_OBJECT (scenario, "Buffering not executing any action"); + + return G_SOURCE_CONTINUE; + } + + if (priv->changing_state) { + GST_DEBUG_OBJECT (scenario, "Changing state, not executing any action"); + return G_SOURCE_CONTINUE; + } + + /* TODO what about non flushing seeks? */ + if (priv->last_seek && priv->target_state > GST_STATE_READY) { + GST_LOG_OBJECT (scenario, "Still seeking -- not executing action"); + return G_SOURCE_CONTINUE; + } + + if (scenario->priv->actions) + act = scenario->priv->actions->data; + + if (act) { + if (act->priv->state == GST_VALIDATE_EXECUTE_ACTION_OK && act->repeat <= 0) { + tmp = priv->actions; + priv->actions = g_list_remove_link (priv->actions, tmp); + + GST_INFO_OBJECT (scenario, "Action %" GST_PTR_FORMAT " is DONE now" + " executing next", act->structure); + + gst_validate_action_unref (act); + g_list_free (tmp); + + if (scenario->priv->actions) { + act = scenario->priv->actions->data; + } else { + _check_scenario_is_done (scenario); + act = NULL; + } + } else if (act->priv->state == GST_VALIDATE_EXECUTE_ACTION_ASYNC) { + GST_LOG_OBJECT (scenario, "Action %" GST_PTR_FORMAT " still running", + act->structure); + + return G_SOURCE_CONTINUE; + } + } + + if (!_check_position (scenario, act, &position)) + return G_SOURCE_CONTINUE; + + if (!_should_execute_action (scenario, act, position, rate)) { + _add_execute_actions_gsource (scenario); + + return G_SOURCE_CONTINUE; + } + + type = _find_action_type (act->type); + + if (act->repeat == -1 && + !gst_structure_get_int (act->structure, "repeat", &act->repeat)) { + gchar *error = NULL; + const gchar *repeat_expr = gst_structure_get_string (act->structure, + "repeat"); + + if (repeat_expr) { + act->repeat = + gst_validate_utils_parse_expression (repeat_expr, + _set_variable_func, scenario, &error); + } + } + + GST_DEBUG_OBJECT (scenario, "Executing %" GST_PTR_FORMAT + " at %" GST_TIME_FORMAT, act->structure, GST_TIME_ARGS (position)); + priv->seeked_in_pause = FALSE; + + act->priv->state = gst_validate_execute_action (type, act); + if (act->priv->state == GST_VALIDATE_EXECUTE_ACTION_ERROR) { + gchar *str = gst_structure_to_string (act->structure); + + GST_VALIDATE_REPORT (scenario, + SCENARIO_ACTION_EXECUTION_ERROR, "Could not execute %s", str); + + g_free (str); + } + + if (act->repeat > 0 && !gst_validate_action_is_subaction (act)) { + act->repeat--; + } + + if (act->priv->state == GST_VALIDATE_EXECUTE_ACTION_OK) { + act->priv->state = _execute_sub_action_action (act); + } + + if (act->priv->state != GST_VALIDATE_EXECUTE_ACTION_ASYNC) { + tmp = priv->actions; + priv->actions = g_list_remove_link (priv->actions, tmp); + + if (act->priv->state != GST_VALIDATE_EXECUTE_ACTION_INTERLACED) + gst_validate_action_unref (act); + else { + SCENARIO_LOCK (scenario); + priv->interlaced_actions = g_list_append (priv->interlaced_actions, act); + SCENARIO_UNLOCK (scenario); + } + + if (priv->actions == NULL) + _check_scenario_is_done (scenario); + + g_list_free (tmp); + + /* Recurse to the next action if it is possible + * to execute right away */ + if (!scenario->priv->execute_on_idle) { + GST_DEBUG_OBJECT (scenario, "linking next action execution"); + + return execute_next_action (scenario); + } else { + _add_execute_actions_gsource (scenario); + GST_DEBUG_OBJECT (scenario, "Executing only on idle, waiting for" + " next dispatch"); + + return G_SOURCE_CONTINUE; + } + } else { + GST_DEBUG_OBJECT (scenario, "Remove source, waiting for action" + " to be done."); + + SCENARIO_LOCK (scenario); + priv->execute_actions_source_id = 0; + SCENARIO_UNLOCK (scenario); + + return G_SOURCE_CONTINUE; + } + + return G_SOURCE_CONTINUE; +} + +static gboolean +stop_waiting (GstValidateAction * action) +{ + GstValidateScenario *scenario = action->scenario; + + gst_validate_printf (action->scenario, "Stop waiting\n"); + + SCENARIO_LOCK (scenario); + scenario->priv->wait_id = 0; + SCENARIO_UNLOCK (scenario); + + gst_validate_action_set_done (action); + _add_execute_actions_gsource (scenario); + + + return G_SOURCE_REMOVE; +} + +static GstElement *_get_target_element (GstValidateScenario * scenario, + GstValidateAction * action); + +static void +stop_waiting_signal (GstBin * bin, GstElement * element, + GstValidateAction * action) +{ + GstValidateScenario *scenario = action->scenario; + GstValidateScenarioPrivate *priv = scenario->priv; + + gst_validate_printf (scenario, "Stop waiting for signal\n"); + + g_signal_handler_disconnect (bin, priv->signal_handler_id); + + priv->signal_handler_id = 0; + gst_validate_action_set_done (action); + _add_execute_actions_gsource (scenario); +} + +static GstValidateExecuteActionReturn +_execute_timed_wait (GstValidateScenario * scenario, GstValidateAction * action) +{ + GstValidateScenarioPrivate *priv = scenario->priv; + GstClockTime duration; + + gdouble wait_multiplier = 1; + const gchar *str_wait_multiplier = + g_getenv ("GST_VALIDATE_SCENARIO_WAIT_MULTIPLIER"); + + if (str_wait_multiplier) { + errno = 0; + wait_multiplier = g_ascii_strtod (str_wait_multiplier, NULL); + + if (errno) { + GST_ERROR ("Could not use the WAIT MULTIPLIER"); + + wait_multiplier = 1; + } + + if (wait_multiplier == 0) { + GST_INFO_OBJECT (scenario, "I have been told not to wait..."); + return GST_VALIDATE_EXECUTE_ACTION_OK; + } + } + + if (!gst_validate_action_get_clocktime (scenario, action, + "duration", &duration)) { + GST_DEBUG_OBJECT (scenario, "Duration could not be parsed"); + + return GST_VALIDATE_EXECUTE_ACTION_ERROR; + } + + duration *= wait_multiplier; + + SCENARIO_LOCK (scenario); + if (priv->execute_actions_source_id) { + g_source_remove (priv->execute_actions_source_id); + priv->execute_actions_source_id = 0; + } + SCENARIO_UNLOCK (scenario); + + SCENARIO_LOCK (scenario); + priv->wait_id = g_timeout_add (duration / G_USEC_PER_SEC, + (GSourceFunc) stop_waiting, action); + SCENARIO_UNLOCK (scenario); + + return GST_VALIDATE_EXECUTE_ACTION_ASYNC; +} + +static GstValidateExecuteActionReturn +_execute_wait_for_signal (GstValidateScenario * scenario, + GstValidateAction * action) +{ + GstValidateScenarioPrivate *priv = scenario->priv; + const gchar *signal_name = gst_structure_get_string + (action->structure, "signal-name"); + GstElement *target; + + if (signal_name == NULL) { + GST_ERROR ("No signal-name given for wait action"); + return GST_VALIDATE_EXECUTE_ACTION_ERROR; + } + + if (scenario->pipeline == NULL) { + GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR, + "Can't execute a 'wait for signal' action after the pipeline " + " has been destroyed."); + + return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED; + } + + target = _get_target_element (scenario, action); + if (target == NULL) { + return FALSE; + } + + gst_validate_printf (action, "Waiting for '%s' signal\n", signal_name); + + if (priv->execute_actions_source_id) { + g_source_remove (priv->execute_actions_source_id); + priv->execute_actions_source_id = 0; + } + + priv->signal_handler_id = + g_signal_connect (target, signal_name, (GCallback) stop_waiting_signal, + action); + + gst_object_unref (target); + + return GST_VALIDATE_EXECUTE_ACTION_ASYNC; +} + +static gboolean +_execute_wait_for_message (GstValidateScenario * scenario, + GstValidateAction * action) +{ + GstValidateScenarioPrivate *priv = scenario->priv; + const gchar *message_type = gst_structure_get_string + (action->structure, "message-type"); + + if (scenario->pipeline == NULL) { + GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR, + "Can't execute a 'wait for message' action after the pipeline " + " has been destroyed."); + + return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED; + } + + gst_validate_printf (action, "Waiting for '%s' message\n", message_type); + + if (priv->execute_actions_source_id) { + g_source_remove (priv->execute_actions_source_id); + priv->execute_actions_source_id = 0; + } + + priv->message_type = g_strdup (message_type); + + return GST_VALIDATE_EXECUTE_ACTION_ASYNC; +} + +static GstValidateExecuteActionReturn +_execute_wait (GstValidateScenario * scenario, GstValidateAction * action) +{ + if (gst_structure_has_field (action->structure, "signal-name")) { + return _execute_wait_for_signal (scenario, action); + } else if (gst_structure_has_field (action->structure, "message-type")) { + return _execute_wait_for_message (scenario, action); + } else { + return _execute_timed_wait (scenario, action); + } + + return FALSE; +} + +static gboolean +_execute_dot_pipeline (GstValidateScenario * scenario, + GstValidateAction * action) +{ + gchar *dotname; + gint details = GST_DEBUG_GRAPH_SHOW_ALL; + + const gchar *name = gst_structure_get_string (action->structure, "name"); + + gst_structure_get_int (action->structure, "details", &details); + if (name) + dotname = g_strdup_printf ("validate.action.%s", name); + else + dotname = g_strdup ("validate.action.unnamed"); + + GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (scenario->pipeline), + details, dotname); + + g_free (dotname); + + return TRUE; +} + +static GstElement * +_get_target_element (GstValidateScenario * scenario, GstValidateAction * action) +{ + const gchar *name; + GstElement *target; + + name = gst_structure_get_string (action->structure, "target-element-name"); + if (name == NULL) { + return NULL; + } + + if (strcmp (GST_OBJECT_NAME (scenario->pipeline), name) == 0) { + target = gst_object_ref (scenario->pipeline); + } else { + target = gst_bin_get_by_name (GST_BIN (scenario->pipeline), name); + } + + if (target == NULL) { + GST_ERROR ("Target element with given name (%s) not found", name); + } + return target; +} + +static gint +cmp_klass_name (gconstpointer a, gconstpointer b) +{ + const GValue *v = a; + const GValue *param = b; + GstElement *element = g_value_get_object (v); + const gchar *klass = g_value_get_string (param); + + if (gst_validate_element_has_klass (element, klass)) + return 0; + + return 1; +} + +/** + * _get_target_elements_by_klass: + * @scenario: a #GstValidateScenario + * @action: a #GstValidateAction + * + * Returns all the elements in the pipeline whose the GST_ELEMENT_METADATA_KLASS + * matches the 'target-element-klass' of @action. + * + * Returns: (transfer full) (element-type GstElement): a list of #GstElement + */ +static GList * +_get_target_elements_by_klass (GstValidateScenario * scenario, + GstValidateAction * action) +{ + GList *result = NULL; + GstIterator *it, *filtered; + const gchar *klass; + GValue v = G_VALUE_INIT, param = G_VALUE_INIT; + gboolean done = FALSE; + + klass = gst_structure_get_string (action->structure, "target-element-klass"); + if (klass == NULL) + return NULL; + + if (gst_validate_element_has_klass (scenario->pipeline, klass)) + result = g_list_prepend (result, gst_object_ref (scenario->pipeline)); + + it = gst_bin_iterate_recurse (GST_BIN (scenario->pipeline)); + + g_value_init (¶m, G_TYPE_STRING); + g_value_set_string (¶m, klass); + + filtered = gst_iterator_filter (it, cmp_klass_name, ¶m); + + while (!done) { + switch (gst_iterator_next (filtered, &v)) { + case GST_ITERATOR_OK:{ + GstElement *child = g_value_get_object (&v); + + if (g_list_find (result, child) == NULL) + result = g_list_prepend (result, gst_object_ref (child)); + g_value_reset (&v); + } + break; + case GST_ITERATOR_RESYNC: + gst_iterator_resync (it); + break; + case GST_ITERATOR_ERROR: + case GST_ITERATOR_DONE: + done = TRUE; + } + } + + g_value_reset (&v); + g_value_reset (¶m); + gst_iterator_free (filtered); + + return result; +} + +static gboolean +_object_set_property (GObject * object, const gchar * property, + const GValue * value, gboolean optional) +{ + GObjectClass *klass = G_OBJECT_GET_CLASS (object); + GParamSpec *paramspec; + + paramspec = g_object_class_find_property (klass, property); + if (paramspec == NULL) { + if (optional) + return TRUE; + GST_ERROR ("Target doesn't have property %s", property); + return FALSE; + } + + g_object_set_property (object, property, value); + + return TRUE; +} + +static gboolean +_execute_set_property (GstValidateScenario * scenario, + GstValidateAction * action) +{ + GstElement *target; + GList *targets = NULL, *l; + const gchar *property; + const GValue *property_value; + gboolean ret = TRUE; + + /* set-property can be applied on either: + * - a single element having target-element-name as name + * - all the elements having target-element-klass as klass + */ + if (gst_structure_get_string (action->structure, "target-element-name")) { + target = _get_target_element (scenario, action); + if (target == NULL) { + return FALSE; + } + targets = g_list_append (targets, target); + } else if (gst_structure_get_string (action->structure, + "target-element-klass")) { + targets = _get_target_elements_by_klass (scenario, action); + } else { + g_assert_not_reached (); + } + + property = gst_structure_get_string (action->structure, "property-name"); + property_value = gst_structure_get_value (action->structure, + "property-value"); + + for (l = targets; l != NULL; l = g_list_next (l)) { + if (!_object_set_property (G_OBJECT (l->data), property, property_value, + action->priv->optional)) + ret = FALSE; + } + + g_list_free_full (targets, gst_object_unref); + return ret; +} + +static gboolean +_execute_set_debug_threshold (GstValidateScenario * scenario, + GstValidateAction * action) +{ + gchar *str = NULL; + gboolean reset = TRUE; + const gchar *threshold_str; + + threshold_str = + gst_structure_get_string (action->structure, "debug-threshold"); + if (threshold_str == NULL) { + gint threshold; + + if (gst_structure_get_int (action->structure, "debug-threshold", + &threshold)) + threshold_str = str = g_strdup_printf ("%i", threshold); + else + return FALSE; + } + + gst_structure_get_boolean (action->structure, "reset", &reset); + + gst_debug_set_threshold_from_string (threshold_str, reset); + + if (str) + g_free (str); + + return TRUE; +} + +static gboolean +_execute_emit_signal (GstValidateScenario * scenario, + GstValidateAction * action) +{ + GstElement *target; + const gchar *signal_name; + + target = _get_target_element (scenario, action); + if (target == NULL) { + return FALSE; + } + + signal_name = gst_structure_get_string (action->structure, "signal-name"); + + /* Right now we don't support arguments to signals as there weren't any use + * cases to cover yet but it should be possible to do so */ + g_signal_emit_by_name (target, signal_name, NULL); + + gst_object_unref (target); + return TRUE; +} + +static GstValidateExecuteActionReturn +_execute_disable_plugin (GstValidateScenario * scenario, + GstValidateAction * action) +{ + GstPlugin *plugin; + const gchar *plugin_name; + + plugin_name = gst_structure_get_string (action->structure, "plugin-name"); + + plugin = gst_registry_find_plugin (gst_registry_get (), plugin_name); + + if (plugin == NULL) { + GST_VALIDATE_REPORT (action->scenario, SCENARIO_ACTION_EXECUTION_ERROR, + "Could not find plugin to disable: %s", plugin_name); + + return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED; + } + + gst_registry_remove_plugin (gst_registry_get (), plugin); + + return GST_VALIDATE_EXECUTE_ACTION_OK; +} + +static void +gst_validate_scenario_update_segment_from_seek (GstValidateScenario * scenario, + GstEvent * seek) +{ + GstValidateScenarioPrivate *priv = scenario->priv; + gint64 start, stop; + GstSeekType start_type, stop_type; + + gst_event_parse_seek (seek, NULL, NULL, NULL, &start_type, &start, + &stop_type, &stop); + + if (start_type == GST_SEEK_TYPE_SET) { + priv->segment_start = start; + } else if (start_type == GST_SEEK_TYPE_END) { + /* TODO fill me */ + } + + if (stop_type == GST_SEEK_TYPE_SET) { + priv->segment_stop = stop; + } else if (start_type == GST_SEEK_TYPE_END) { + /* TODO fill me */ + } +} + +static gint +_compare_actions (GstValidateAction * a, GstValidateAction * b) +{ + if (a->action_number < b->action_number) + return -1; + else if (a->action_number == b->action_number) + return 0; + + return 1; +} + +static gboolean +gst_validate_action_default_prepare_func (GstValidateAction * action) +{ + gulong i; + GstClockTime time; + const gchar *vars[] = { "duration", "start", "stop" }; + + for (i = 0; i < G_N_ELEMENTS (vars); i++) { + gint res = + gst_validate_action_get_clocktime (action->scenario, action, vars[i], + &time); + if (res == FALSE) { + GST_ERROR_OBJECT (action->scenario, "Could not get clocktime for" + " variable %s", vars[i]); + + return FALSE; + } else if (res == -1) { + continue; + } + + gst_structure_set (action->structure, vars[i], GST_TYPE_CLOCK_TIME, + time, NULL); + } + + return TRUE; +} + +static void +_check_waiting_for_message (GstValidateScenario * scenario, + GstMessage * message) +{ + GstValidateScenarioPrivate *priv = scenario->priv; + + if (!g_strcmp0 (priv->message_type, + gst_message_type_get_name (GST_MESSAGE_TYPE (message)))) { + GstValidateAction *action = scenario->priv->actions->data; + + g_free ((gpointer) priv->message_type); + priv->message_type = NULL; + + gst_validate_printf (scenario, "Stop waiting for message\n"); + + gst_validate_action_set_done (action); + _add_execute_actions_gsource (scenario); + } +} + +static gboolean +message_cb (GstBus * bus, GstMessage * message, GstValidateScenario * scenario) +{ + gboolean is_error = FALSE; + GstValidateScenarioPrivate *priv = scenario->priv; + + switch (GST_MESSAGE_TYPE (message)) { + case GST_MESSAGE_ASYNC_DONE: + if (priv->last_seek) { + gst_validate_scenario_update_segment_from_seek (scenario, + priv->last_seek); + + if (priv->target_state == GST_STATE_PAUSED) + priv->seeked_in_pause = TRUE; + + gst_event_replace (&priv->last_seek, NULL); + gst_validate_action_set_done (priv->actions->data); + } + + if (priv->needs_parsing) { + GList *tmp; + + for (tmp = priv->needs_parsing; tmp; tmp = tmp->next) { + GstValidateAction *action = tmp->data; + + if (!_set_action_playback_time (scenario, action)) + return FALSE; + + priv->actions = g_list_insert_sorted (priv->actions, action, + (GCompareFunc) _compare_actions); + } + + g_list_free (priv->needs_parsing); + priv->needs_parsing = NULL; + } + _add_execute_actions_gsource (scenario); + break; + case GST_MESSAGE_STATE_CHANGED: + { + if (GST_MESSAGE_SRC (message) == GST_OBJECT (scenario->pipeline)) { + GstState nstate, pstate; + + gst_message_parse_state_changed (message, &pstate, &nstate, NULL); + + if (scenario->priv->changing_state && + scenario->priv->target_state == nstate) { + if (scenario->priv->actions && + _action_sets_state (scenario->priv->actions->data)) + gst_validate_action_set_done (priv->actions->data); + scenario->priv->changing_state = FALSE; + } + + if (pstate == GST_STATE_READY && nstate == GST_STATE_PAUSED) + _add_execute_actions_gsource (scenario); + } + break; + } + case GST_MESSAGE_ERROR: + is_error = TRUE; + + /* Passtrough */ + case GST_MESSAGE_EOS: + { + GstValidateAction *stop_action; + GstValidateActionType *stop_action_type; + GstStructure *s; + + if (!is_error) { + priv->got_eos = TRUE; + if (priv->message_type) { + + if (priv->actions->next) { + GST_DEBUG_OBJECT (scenario, + "Waiting for a message and got a next action" + " to execute, letting it a chance!"); + goto done; + } else { + /* Clear current message wait if waiting for EOS */ + _check_waiting_for_message (scenario, message); + } + } + } + + SCENARIO_LOCK (scenario); + if (scenario->priv->actions || scenario->priv->interlaced_actions || + scenario->priv->on_addition_actions) { + guint nb_actions = 0; + gchar *actions = g_strdup (""), *tmpconcat; + GList *tmp; + GList *all_actions = + g_list_concat (g_list_concat (scenario->priv->actions, + scenario->priv->interlaced_actions), + scenario->priv->on_addition_actions); + + for (tmp = all_actions; tmp; tmp = tmp->next) { + gchar *action_string; + GstValidateAction *action = ((GstValidateAction *) tmp->data); + GstValidateActionType *type = _find_action_type (action->type); + + tmpconcat = actions; + + if (type->flags & GST_VALIDATE_ACTION_TYPE_NO_EXECUTION_NOT_FATAL || + action->priv->state == GST_VALIDATE_EXECUTE_ACTION_OK || + action->priv->optional) { + gst_validate_action_unref (action); + + continue; + } + + nb_actions++; + + action_string = gst_structure_to_string (action->structure); + actions = + g_strdup_printf ("%s\n%*s%s", actions, 20, "", action_string); + gst_validate_action_unref (action); + g_free (tmpconcat); + g_free (action_string); + } + g_list_free (all_actions); + scenario->priv->actions = NULL; + scenario->priv->interlaced_actions = NULL; + scenario->priv->on_addition_actions = NULL; + + if (nb_actions > 0) + GST_VALIDATE_REPORT (scenario, SCENARIO_NOT_ENDED, + "%i actions were not executed: %s", nb_actions, actions); + g_free (actions); + } + SCENARIO_UNLOCK (scenario); + + GST_DEBUG_OBJECT (scenario, "Got EOS; generate 'stop' action"); + + stop_action_type = _find_action_type ("stop"); + stop_action = gst_validate_action_new (scenario, stop_action_type); + s = gst_structure_from_string ("stop;", NULL); + _fill_action (scenario, stop_action, s, FALSE); + gst_structure_free (s); + gst_validate_execute_action (stop_action_type, stop_action); + gst_mini_object_unref (GST_MINI_OBJECT (stop_action)); + + break; + } + case GST_MESSAGE_BUFFERING: + { + gint percent; + + gst_message_parse_buffering (message, &percent); + + if (percent == 100) + priv->buffering = FALSE; + else + priv->buffering = TRUE; + + g_print ("%s %d%% \r", "Buffering...", percent); + break; + } + default: + break; + } + +done: + /* Check if we got the message expected by a wait action */ + if (priv->message_type) + _check_waiting_for_message (scenario, message); + + return TRUE; +} + +static void +_pipeline_freed_cb (GstValidateScenario * scenario, + GObject * where_the_object_was) +{ + scenario->pipeline = NULL; + + GST_DEBUG_OBJECT (scenario, "pipeline was freed"); +} + +static gboolean +_load_scenario_file (GstValidateScenario * scenario, + const gchar * scenario_file, gboolean * is_config) +{ + gboolean ret = TRUE; + GList *structures, *tmp; + GstValidateScenarioPrivate *priv = scenario->priv; + + *is_config = FALSE; + + structures = gst_validate_utils_structs_parse_from_filename (scenario_file); + if (structures == NULL) + goto failed; + + for (tmp = structures; tmp; tmp = tmp->next) { + GstValidateAction *action; + GstValidateActionType *action_type; + const gchar *type; + + GstStructure *structure = tmp->data; + + + type = gst_structure_get_name (structure); + if (!g_strcmp0 (type, "description")) { + gst_structure_get_boolean (structure, "is-config", is_config); + gst_structure_get_boolean (structure, "handles-states", + &priv->handles_state); + + priv->description = gst_structure_copy (structure); + + continue; + } else if (!(action_type = _find_action_type (type))) { + if (gst_structure_has_field (structure, "optional-action-type")) { + GST_INFO_OBJECT (scenario, + "Action type not found %s but marked as not mandatory", type); + continue; + } + + GST_ERROR_OBJECT (scenario, "We do not handle action types %s", type); + goto failed; + } + + if (action_type->parameters) { + guint i; + + for (i = 0; action_type->parameters[i].name; i++) { + if (action_type->parameters[i].mandatory && + gst_structure_has_field (structure, + action_type->parameters[i].name) == FALSE) { + GST_ERROR_OBJECT (scenario, + "Mandatory field '%s' not present in structure: %" GST_PTR_FORMAT, + action_type->parameters[i].name, structure); + goto failed; + } + } + } + + action = gst_validate_action_new (scenario, action_type); + if (_fill_action (scenario, action, + structure, TRUE) == GST_VALIDATE_EXECUTE_ACTION_ERROR) + goto failed; + + action->action_number = priv->num_actions++; + } + +done: + g_list_free_full (structures, (GDestroyNotify) gst_structure_free); + + return ret; + +failed: + ret = FALSE; + + goto done; +} + +static gboolean +gst_validate_scenario_load (GstValidateScenario * scenario, + const gchar * scenario_name) +{ + gchar **scenarios = NULL; + guint i; + gchar *lfilename = NULL, *tldir = NULL; + gboolean found_actions = FALSE, is_config, ret = TRUE; + const gchar *scenarios_path = g_getenv ("GST_VALIDATE_SCENARIOS_PATH"); + + gchar **env_scenariodir = + scenarios_path ? g_strsplit (scenarios_path, ":", 0) : NULL; + + if (!scenario_name) + goto invalid_name; + + scenarios = g_strsplit (scenario_name, ":", -1); + + for (i = 0; scenarios[i]; i++) { + + /* First check if the scenario name is not a full path to the + * actual scenario */ + if (g_file_test (scenarios[i], G_FILE_TEST_IS_REGULAR)) { + GST_DEBUG_OBJECT (scenario, "Scenario: %s is a full path to a scenario " + "trying to load it", scenarios[i]); + if ((ret = _load_scenario_file (scenario, scenario_name, &is_config))) + goto check_scenario; + } + + lfilename = + g_strdup_printf ("%s" GST_VALIDATE_SCENARIO_SUFFIX, scenarios[i]); + + tldir = g_build_filename ("data", "scenarios", lfilename, NULL); + + if ((ret = _load_scenario_file (scenario, tldir, &is_config))) + goto check_scenario; + + g_free (tldir); + + if (env_scenariodir) { + guint i; + + for (i = 0; env_scenariodir[i]; i++) { + tldir = g_build_filename (env_scenariodir[i], lfilename, NULL); + if ((ret = _load_scenario_file (scenario, tldir, &is_config))) + goto check_scenario; + g_free (tldir); + } + } + + /* Try from local profiles */ + tldir = + g_build_filename (g_get_user_data_dir (), + "gstreamer-" GST_API_VERSION, "validate", + GST_VALIDATE_SCENARIO_DIRECTORY, lfilename, NULL); + + if (!(ret = _load_scenario_file (scenario, tldir, &is_config))) { + g_free (tldir); + /* Try from system-wide profiles */ + tldir = g_build_filename (GST_DATADIR, "gstreamer-" GST_API_VERSION, + "validate", GST_VALIDATE_SCENARIO_DIRECTORY, lfilename, NULL); + + if (!(ret = _load_scenario_file (scenario, tldir, &is_config))) { + goto error; + } + } + /* else check scenario */ + check_scenario: + if (tldir) + g_free (tldir); + if (lfilename) + g_free (lfilename); + + if (!is_config) { + if (found_actions == TRUE) + goto one_actions_scenario_max; + else + found_actions = TRUE; + } + } + +done: + + if (env_scenariodir) + g_strfreev (env_scenariodir); + + g_strfreev (scenarios); + + if (ret == FALSE) + g_error ("Could not set scenario %s => EXIT\n", scenario_name); + + return ret; + +invalid_name: + { + GST_ERROR ("Invalid name for scenario '%s'", scenario_name); + error: + ret = FALSE; + goto done; + } +one_actions_scenario_max: + { + GST_ERROR ("You can set at most only one action scenario. " + "You can have several config scenarios though (a config scenario's " + "file must have is-config=true, and all its actions must be executable " + "at parsing time)."); + ret = FALSE; + goto done; + + } +} + + +static void +gst_validate_scenario_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstValidateScenario *self = GST_VALIDATE_SCENARIO (object); + + switch (prop_id) { + case PROP_RUNNER: + /* we assume the runner is valid as long as this scenario is, + * no ref taken */ + gst_validate_reporter_set_runner (GST_VALIDATE_REPORTER (object), + g_value_get_object (value)); + break; + case PROP_HANDLES_STATE: + g_assert_not_reached (); + break; + case PROP_EXECUTE_ON_IDLE: + self->priv->execute_on_idle = g_value_get_boolean (value); + break; + default: + break; + } +} + +static void +gst_validate_scenario_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstValidateScenario *self = GST_VALIDATE_SCENARIO (object); + + switch (prop_id) { + case PROP_RUNNER: + /* we assume the runner is valid as long as this scenario is, + * no ref taken */ + g_value_set_object (value, + gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER (object))); + break; + case PROP_HANDLES_STATE: + g_value_set_boolean (value, self->priv->handles_state); + break; + case PROP_EXECUTE_ON_IDLE: + g_value_set_boolean (value, self->priv->execute_on_idle); + break; + default: + break; + } +} + +static void +gst_validate_scenario_class_init (GstValidateScenarioClass * klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (GstValidateScenarioPrivate)); + + object_class->dispose = gst_validate_scenario_dispose; + object_class->finalize = gst_validate_scenario_finalize; + + object_class->get_property = gst_validate_scenario_get_property; + object_class->set_property = gst_validate_scenario_set_property; + + g_object_class_install_property (object_class, PROP_RUNNER, + g_param_spec_object ("validate-runner", "VALIDATE Runner", + "The Validate runner to " "report errors to", + GST_TYPE_VALIDATE_RUNNER, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_HANDLES_STATE, + g_param_spec_boolean ("handles-states", "Handles state", + "True if the application should not set handle the first state change " + " False if it is application responsibility", + FALSE, G_PARAM_READABLE)); + + g_object_class_install_property (object_class, + PROP_EXECUTE_ON_IDLE, + g_param_spec_boolean ("execute-on-idle", + "Force waiting between actions", + "Always execute actions on idle and do not chain them" + " to execute as fast as possible. That is usefull if action execution" + " can lead to the addition of source on the same main loop." + " It allows those other GSources to have a chance to be dispatch between" + " validate actions execution", FALSE, G_PARAM_READWRITE)); + + /** + * GstValidateScenario::done: + * @scenario: The scenario runing + * + * Emitted once all actions have been executed + */ + scenario_signals[DONE] = + g_signal_new ("done", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); +} + +static void +gst_validate_scenario_init (GstValidateScenario * scenario) +{ + GstValidateScenarioPrivate *priv = scenario->priv = + GST_VALIDATE_SCENARIO_GET_PRIVATE (scenario); + + priv->seek_pos_tol = DEFAULT_SEEK_TOLERANCE; + priv->segment_start = 0; + priv->segment_stop = GST_CLOCK_TIME_NONE; + + g_mutex_init (&priv->lock); +} + +static void +gst_validate_scenario_dispose (GObject * object) +{ + GstValidateScenarioPrivate *priv = GST_VALIDATE_SCENARIO (object)->priv; + + if (priv->last_seek) + gst_event_unref (priv->last_seek); + if (GST_VALIDATE_SCENARIO (object)->pipeline) + g_object_weak_unref (G_OBJECT (GST_VALIDATE_SCENARIO (object)->pipeline), + (GWeakNotify) _pipeline_freed_cb, object); + + if (priv->bus) { + gst_bus_remove_signal_watch (priv->bus); + gst_object_unref (priv->bus); + priv->bus = NULL; + } + + G_OBJECT_CLASS (gst_validate_scenario_parent_class)->dispose (object); +} + +static void +gst_validate_scenario_finalize (GObject * object) +{ + GstValidateScenarioPrivate *priv = GST_VALIDATE_SCENARIO (object)->priv; + + g_list_free_full (priv->actions, (GDestroyNotify) gst_mini_object_unref); + g_list_free_full (priv->interlaced_actions, + (GDestroyNotify) gst_mini_object_unref); + g_list_free_full (priv->on_addition_actions, + (GDestroyNotify) gst_mini_object_unref); + if (priv->description) + gst_structure_free (priv->description); + g_mutex_clear (&priv->lock); + + G_OBJECT_CLASS (gst_validate_scenario_parent_class)->finalize (object); +} + +static void _element_added_cb (GstBin * bin, GstElement * element, + GstValidateScenario * scenario); + +static void +iterate_children (GstValidateScenario * scenario, GstBin * bin) +{ + GstIterator *it; + GValue v = G_VALUE_INIT; + gboolean done = FALSE; + GHashTable *called; /* set of GstElement on which we already called _element_added_cb() */ + + called = g_hash_table_new (NULL, NULL); + it = gst_bin_iterate_elements (bin); + + while (!done) { + switch (gst_iterator_next (it, &v)) { + case GST_ITERATOR_OK:{ + GstElement *child = g_value_get_object (&v); + + if (g_hash_table_lookup (called, child) == NULL) { + _element_added_cb (bin, child, scenario); + g_hash_table_add (called, child); + } + g_value_reset (&v); + } + break; + case GST_ITERATOR_RESYNC: + gst_iterator_resync (it); + break; + case GST_ITERATOR_ERROR: + case GST_ITERATOR_DONE: + done = TRUE; + } + } + g_value_reset (&v); + gst_iterator_free (it); + g_hash_table_unref (called); +} + +static gboolean +should_execute_action (GstElement * element, GstValidateAction * action) +{ + const gchar *tmp; + + tmp = gst_structure_get_string (action->structure, "target-element-name"); + if (tmp != NULL && !strcmp (tmp, GST_ELEMENT_NAME (element))) + return TRUE; + + tmp = gst_structure_get_string (action->structure, "target-element-klass"); + if (tmp != NULL && gst_validate_element_has_klass (element, tmp)) + return TRUE; + + return FALSE; +} + +static void +_element_added_cb (GstBin * bin, GstElement * element, + GstValidateScenario * scenario) +{ + GList *tmp; + + GstValidateScenarioPrivate *priv = scenario->priv; + + /* Check if it's an element we track for a set-property action */ + SCENARIO_LOCK (scenario); + tmp = priv->on_addition_actions; + while (tmp) { + GstValidateAction *action = (GstValidateAction *) tmp->data; + + if (action->playback_time != GST_CLOCK_TIME_NONE) + break; + if (g_strcmp0 (action->type, "set-property")) + break; + + GST_DEBUG_OBJECT (bin, "Checking action #%d %p (%s)", action->action_number, + action, action->type); + if (should_execute_action (element, action)) { + GstValidateActionType *action_type; + action_type = _find_action_type (action->type); + GST_DEBUG_OBJECT (element, "Executing set-property action"); + if (action_type->execute (scenario, action)) { + priv->on_addition_actions = + g_list_remove_link (priv->on_addition_actions, tmp); + gst_mini_object_unref (GST_MINI_OBJECT (action)); + g_list_free (tmp); + tmp = priv->on_addition_actions; + } else + tmp = tmp->next; + } else + tmp = tmp->next; + } + SCENARIO_UNLOCK (scenario); + + _check_scenario_is_done (scenario); + + /* If it's a bin, listen to the child */ + if (GST_IS_BIN (element)) { + g_signal_connect (element, "element-added", (GCallback) _element_added_cb, + scenario); + iterate_children (scenario, GST_BIN (element)); + } +} + +/** + * gst_validate_scenario_factory_create: + * @runner: The #GstValidateRunner to use to report issues + * @pipeline: The pipeline to run the scenario on + * @scenario_name: The name (or path) of the scenario to run + * + * Returns: (transfer full): A #GstValidateScenario or NULL + */ +GstValidateScenario * +gst_validate_scenario_factory_create (GstValidateRunner * + runner, GstElement * pipeline, const gchar * scenario_name) +{ + GstValidateScenario *scenario = + g_object_new (GST_TYPE_VALIDATE_SCENARIO, "validate-runner", + runner, NULL); + + GST_LOG ("Creating scenario %s", scenario_name); + if (!gst_validate_scenario_load (scenario, scenario_name)) { + g_object_unref (scenario); + + return NULL; + } + + if (scenario->priv->description) { + const gchar *pipeline_name = + gst_structure_get_string (scenario->priv->description, + "pipeline-name"); + + if (pipeline_name && !g_pattern_match_simple (pipeline_name, + GST_OBJECT_NAME (pipeline))) { + GST_INFO ("Scenario %s only applies on pipeline %s not %s", + scenario_name, pipeline_name, GST_OBJECT_NAME (pipeline)); + + gst_object_unref (scenario); + + return NULL; + } + } + + scenario->pipeline = pipeline; + g_object_weak_ref (G_OBJECT (pipeline), + (GWeakNotify) _pipeline_freed_cb, scenario); + gst_validate_reporter_set_name (GST_VALIDATE_REPORTER (scenario), + g_strdup (scenario_name)); + + g_signal_connect (pipeline, "element-added", (GCallback) _element_added_cb, + scenario); + + iterate_children (scenario, GST_BIN (pipeline)); + + scenario->priv->bus = gst_element_get_bus (pipeline); + gst_bus_add_signal_watch (scenario->priv->bus); + g_signal_connect (scenario->priv->bus, "message", (GCallback) message_cb, + scenario); + + if (scenario->priv->handles_state) { + GST_INFO_OBJECT (scenario, "Scenario handles state," + " Starting the get position source"); + _add_execute_actions_gsource (scenario); + } + + gst_validate_printf (NULL, + "\n=========================================\n" + "Running scenario %s on pipeline %s" + "\n=========================================\n", scenario_name, + GST_OBJECT_NAME (pipeline)); + + scenario->priv->overrides = + gst_validate_override_registry_get_override_for_names + (gst_validate_override_registry_get (), "scenarios", NULL); + + return scenario; +} + +static gboolean +_add_description (GQuark field_id, const GValue * value, KeyFileGroupName * kfg) +{ + gchar *tmp = gst_value_serialize (value); + + g_key_file_set_string (kfg->kf, kfg->group_name, + g_quark_to_string (field_id), g_strcompress (tmp)); + + g_free (tmp); + + return TRUE; +} + + +static gboolean +_parse_scenario (GFile * f, GKeyFile * kf) +{ + gboolean ret = FALSE; + gchar *fname = g_file_get_basename (f); + + if (g_str_has_suffix (fname, GST_VALIDATE_SCENARIO_SUFFIX)) { + gboolean needs_clock_sync = FALSE; + GstStructure *desc = NULL; + + gchar **name = g_strsplit (fname, GST_VALIDATE_SCENARIO_SUFFIX, 0); + GList *tmp, *structures = structs_parse_from_gfile (f); + + for (tmp = structures; tmp; tmp = tmp->next) { + GstValidateActionType *type = + _find_action_type (gst_structure_get_name (tmp->data)); + + if (gst_structure_has_name (tmp->data, "description")) + desc = gst_structure_copy (tmp->data); + else if (type && type->flags & GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK) + needs_clock_sync = TRUE; + } + + if (needs_clock_sync) { + if (desc) + gst_structure_set (desc, "need-clock-sync", G_TYPE_BOOLEAN, TRUE, NULL); + else + desc = gst_structure_from_string ("description, need-clock-sync=true;", + NULL); + } + + if (desc) { + KeyFileGroupName kfg; + + kfg.group_name = name[0]; + kfg.kf = kf; + + gst_structure_foreach (desc, + (GstStructureForeachFunc) _add_description, &kfg); + gst_structure_free (desc); + } else { + g_key_file_set_string (kf, name[0], "noinfo", "nothing"); + } + g_list_free_full (structures, (GDestroyNotify) gst_structure_free); + g_strfreev (name); + + ret = TRUE; + } + + g_free (fname); + return ret; +} + +static void +_list_scenarios_in_dir (GFile * dir, GKeyFile * kf) +{ + GFileEnumerator *fenum; + GFileInfo *info; + + fenum = g_file_enumerate_children (dir, G_FILE_ATTRIBUTE_STANDARD_NAME, + G_FILE_QUERY_INFO_NONE, NULL, NULL); + + if (fenum == NULL) + return; + + for (info = g_file_enumerator_next_file (fenum, NULL, NULL); + info; info = g_file_enumerator_next_file (fenum, NULL, NULL)) { + GFile *f = g_file_enumerator_get_child (fenum, info); + + _parse_scenario (f, kf); + gst_object_unref (f); + } + + gst_object_unref (fenum); +} + +gboolean +gst_validate_list_scenarios (gchar ** scenarios, gint num_scenarios, + gchar * output_file) +{ + gchar *result; + gsize datalength; + + GError *err = NULL; + GKeyFile *kf = NULL; + gint res = 0; + const gchar *envvar; + gchar **env_scenariodir = NULL; + gchar *tldir = g_build_filename (g_get_user_data_dir (), + "gstreamer-" GST_API_VERSION, "validate", GST_VALIDATE_SCENARIO_DIRECTORY, + NULL); + GFile *dir = g_file_new_for_path (tldir); + + kf = g_key_file_new (); + if (num_scenarios > 0) { + gint i; + GFile *file; + + for (i = 0; i < num_scenarios; i++) { + file = g_file_new_for_path (scenarios[i]); + if (!_parse_scenario (file, kf)) { + GST_ERROR ("Could not parser scenario: %s", scenarios[i]); + + gst_object_unref (file); + res = 1; + } + } + + goto done; + } + + envvar = g_getenv ("GST_VALIDATE_SCENARIOS_PATH"); + if (envvar) + env_scenariodir = g_strsplit (envvar, ":", 0); + + _list_scenarios_in_dir (dir, kf); + g_object_unref (dir); + g_free (tldir); + + tldir = g_build_filename (GST_DATADIR, "gstreamer-" GST_API_VERSION, + "validate", GST_VALIDATE_SCENARIO_DIRECTORY, NULL); + dir = g_file_new_for_path (tldir); + _list_scenarios_in_dir (dir, kf); + g_object_unref (dir); + g_free (tldir); + + if (env_scenariodir) { + guint i; + + for (i = 0; env_scenariodir[i]; i++) { + dir = g_file_new_for_path (env_scenariodir[i]); + _list_scenarios_in_dir (dir, kf); + g_object_unref (dir); + } + } + + /* Hack to make it work uninstalled */ + dir = g_file_new_for_path ("data/scenarios"); + _list_scenarios_in_dir (dir, kf); + g_object_unref (dir); + +done: + result = g_key_file_to_data (kf, &datalength, &err); + g_print ("All scenarios avalaible:\n%s", result); + + if (output_file && !err) + g_file_set_contents (output_file, result, datalength, &err); + + if (env_scenariodir) + g_strfreev (env_scenariodir); + + if (err) { + GST_WARNING ("Got error '%s' listing scenarios", err->message); + g_clear_error (&err); + + res = FALSE; + } + + g_key_file_free (kf); + + return res; +} + +void +gst_validate_action_set_done (GstValidateAction * action) +{ + GstValidateScenario *scenario = action->scenario; + + if (action->priv->state == GST_VALIDATE_EXECUTE_ACTION_INTERLACED) { + + if (action->scenario) { + SCENARIO_LOCK (action->scenario); + action->scenario->priv->interlaced_actions = + g_list_remove (action->scenario->priv->interlaced_actions, action); + SCENARIO_UNLOCK (action->scenario); + } + + gst_validate_action_unref (action); + } + + action->priv->state = _execute_sub_action_action (action); + if (action->priv->state == GST_VALIDATE_EXECUTE_ACTION_ASYNC) { + GST_DEBUG_OBJECT (scenario, "Sub action executed ASYNC"); + + return; + } + + if (scenario) { + if (GPOINTER_TO_INT (g_private_get (&main_thread_priv))) { + if (!scenario->priv->execute_on_idle) { + GST_DEBUG_OBJECT (scenario, "Right thread, executing next?"); + execute_next_action (scenario); + + return; + } else + GST_DEBUG_OBJECT (scenario, "Right thread, but executing only on idle"); + } else + GST_DEBUG_OBJECT (action->scenario, "Not doing anything outside the" + " 'main' thread"); + } + + _add_execute_actions_gsource (scenario); +} + +/** + * gst_validate_register_action_type: + * @type_name: The name of the new action type to add + * @implementer_namespace: The namespace of the implementer of the action type. + * That should always be the name of the GstPlugin as + * retrived with #gst_plugin_get_name when the action type + * is register inside a plugin. + * @function: (scope notified): The function to be called to execute the action + * @parameters: (allow-none) (array zero-terminated=1) (element-type GstValidate.ActionParameter): The #GstValidateActionParameter usable as parameter of the type + * @description: A description of the new type + * @is_config: Whether the action is a config action or not. A config action will + * be executed even before the pipeline starts processing + * + * Register a new action type to the action type system. If the action type already + * exists, it will be overriden by that new definition + * + * Returns: (transfer none): The newly created action type or the already registered action type + * if it had a higher rank + */ +GstValidateActionType * +gst_validate_register_action_type (const gchar * type_name, + const gchar * implementer_namespace, + GstValidateExecuteAction function, + GstValidateActionParameter * parameters, + const gchar * description, GstValidateActionTypeFlags flags) +{ + GstValidateActionType *type = gst_validate_register_action_type_dynamic (NULL, + type_name, GST_RANK_NONE, function, parameters, description, + flags); + + g_free (type->implementer_namespace); + type->implementer_namespace = g_strdup (implementer_namespace); + + return type; +} + +static void +_free_action_types (GList * action_types) +{ + g_list_free_full (action_types, (GDestroyNotify) gst_mini_object_unref); +} + +/** + * gst_validate_register_action_type_dynamic: + * @plugin: (allow-none): The #GstPlugin that register the action type, + * or NULL for a static element. + * @rank: The ranking of that implementation of the action type called + * @type_name. If an action type has been registered with the same + * name with a higher rank, the new implementation will not be used, + * and the already registered action type is returned. + * If the already registered implementation has a lower rank, the + * new implementation will be used and returned. + * @type_name: The name of the new action type to add + * @function: (scope notified): The function to be called to execute the action + * @parameters: (allow-none) (array zero-terminated=1) (element-type GstValidate.ActionParameter): The #GstValidateActionParameter usable as parameter of the type + * @description: A description of the new type + * @flags: The #GstValidateActionTypeFlags to be set on the new action type + * + * Returns: (transfer none): The newly created action type or the already registered action type + * if it had a higher rank + */ +GstValidateActionType * +gst_validate_register_action_type_dynamic (GstPlugin * plugin, + const gchar * type_name, GstRank rank, + GstValidateExecuteAction function, GstValidateActionParameter * parameters, + const gchar * description, GstValidateActionTypeFlags flags) +{ + GstValidateActionType *tmptype; + GstValidateActionType *type = gst_validate_action_type_new (); + gboolean is_config = IS_CONFIG_ACTION_TYPE (flags); + gint n_params = is_config ? 0 : 2; + + if (parameters) { + for (n_params = 0; parameters[n_params].name != NULL; n_params++); + n_params += 1; + } + + if (n_params) { + type->parameters = g_new0 (GstValidateActionParameter, n_params); + } + + if (parameters) { + memcpy (type->parameters, parameters, + sizeof (GstValidateActionParameter) * (n_params)); + } + + type->prepare = gst_validate_action_default_prepare_func; + type->execute = function; + type->name = g_strdup (type_name); + if (plugin) + type->implementer_namespace = g_strdup (gst_plugin_get_name (plugin)); + else + type->implementer_namespace = g_strdup ("none"); + + type->description = g_strdup (description); + type->flags = flags; + type->rank = rank; + + if ((tmptype = _find_action_type (type_name))) { + if (tmptype->rank <= rank) { + action_types = g_list_remove (action_types, tmptype); + type->overriden_type = tmptype; + } else { + gst_mini_object_unref (GST_MINI_OBJECT (type)); + + type = tmptype; + } + } + + if (type != tmptype) + action_types = g_list_append (action_types, type); + + if (plugin) { + GList *plugin_action_types = g_object_steal_data (G_OBJECT (plugin), + "GstValidatePluginActionTypes"); + + plugin_action_types = g_list_prepend (plugin_action_types, + gst_mini_object_ref (GST_MINI_OBJECT (type))); + + g_object_set_data_full (G_OBJECT (plugin), "GstValidatePluginActionTypes", + plugin_action_types, (GDestroyNotify) _free_action_types); + } + + return type; +} + +GstValidateActionType * +gst_validate_get_action_type (const gchar * type_name) +{ + GstValidateActionType *type = _find_action_type (type_name); + + if (type) + return + GST_VALIDATE_ACTION_TYPE (gst_mini_object_ref (GST_MINI_OBJECT (type))); + + return NULL; +} + +static GList * +gst_validate_list_action_types (void) +{ + return action_types; +} + +/** + * gst_validate_print_action_types: + * @wanted_types: (array length=num_wanted_types): (optional): List of types to be printed + * @num_wanted_types: (optional): Length of @wanted_types + * + * Prints the action types details wanted in @wanted_types + * + * Returns: True if all types could be printed + */ +gboolean +gst_validate_print_action_types (const gchar ** wanted_types, + gint num_wanted_types) +{ + GList *tmp; + gint nfound = 0; + + for (tmp = gst_validate_list_action_types (); tmp; tmp = tmp->next) { + GstValidateActionType *atype = tmp->data; + gboolean print = FALSE; + + if (num_wanted_types) { + gint n; + + for (n = 0; n < num_wanted_types; n++) { + if (g_strcmp0 (atype->name, wanted_types[n]) == 0 || + g_strcmp0 (atype->implementer_namespace, wanted_types[n]) == 0) { + nfound++; + print = TRUE; + + break; + } + } + } else { + print = TRUE; + } + + if (print && num_wanted_types) { + gst_validate_printf (tmp->data, "\n"); + } else if (print) { + gchar *desc = + g_regex_replace (newline_regex, atype->description, -1, 0, "\n ", + 0, + NULL); + + gst_validate_printf (NULL, "\n%s: %s:\n %s\n", + atype->implementer_namespace, atype->name, desc); + g_free (desc); + } + } + + if (num_wanted_types && num_wanted_types > nfound) { + return FALSE; + } + + return TRUE; +} + +/** + * gst_validate_scenario_get_actions: + * @scenario: The scenario to retrieve remaining actions for + * + * Get remaining actions from @scenario. + * + * Returns: (transfer full) (element-type GstValidateAction): A list of #GstValidateAction. + */ +GList * +gst_validate_scenario_get_actions (GstValidateScenario * scenario) +{ + if (GPOINTER_TO_INT (g_private_get (&main_thread_priv))) { + return g_list_copy_deep (scenario->priv->actions, + (GCopyFunc) gst_mini_object_ref, NULL); + } else { + GST_WARNING_OBJECT (scenario, "Trying to get next action from outside" + " the 'main' thread"); + } + + return NULL; +} + +void +init_scenarios (void) +{ + GST_DEBUG_CATEGORY_INIT (gst_validate_scenario_debug, "gstvalidatescenario", + GST_DEBUG_FG_YELLOW, "Gst validate scenarios"); + + _gst_validate_action_type = gst_validate_action_get_type (); + _gst_validate_action_type_type = gst_validate_action_type_get_type (); + + g_private_set (&main_thread_priv, GUINT_TO_POINTER (TRUE)); + + /* *INDENT-OFF* */ + REGISTER_ACTION_TYPE ("description", NULL, + ((GstValidateActionParameter []) { + { + .name = "summary", + .description = "Whether the scenario is a config only scenario (ie. explain what it does)", + .mandatory = FALSE, + .types = "sting", + .possible_variables = NULL, + .def = "'Nothing'"}, + { + .name = "is-config", + .description = "Whether the scenario is a config only scenario", + .mandatory = FALSE, + .types = "boolean", + .possible_variables = NULL, + .def = "false" + }, + { + .name = "handles-states", + .description = "Whether the scenario handles pipeline state changes from the beginning\n" + "in that case the application should not set the state of the pipeline to anything\n" + "and the scenario action will be executed from the beginning", + .mandatory = FALSE, + .types = "boolean", + .possible_variables = NULL, + .def = "false"}, + { + .name = "seek", + .description = "Whether the scenario executes seek action or not", + .mandatory = FALSE, + .types = "boolean", + .possible_variables = NULL, + .def = "false" + }, + { + .name = "reverse-playback", + .description = "Whether the scenario plays the stream backward", + .mandatory = FALSE, + .types = "boolean", + .possible_variables = NULL, + .def = "false" + }, + { + .name = "need-clock-sync", + .description = "Whether the scenario needs the execution to be syncronized with the pipeline\n" + "clock. Letting the user know if it can be used with a 'fakesink sync=false' sink", + .mandatory = FALSE, + .types = "boolean", + .possible_variables = NULL, + .def = "false" + }, + { + .name = "min-media-duration", + .description = "Lets the user know the minimum duration of the stream for the scenario\n" + "to be usable", + .mandatory = FALSE, + .types = "double", + .possible_variables = NULL, + .def = "0.0" + }, + { + .name = "min-audio-track", + .description = "Lets the user know the minimum number of audio tracks the stream needs to contain\n" + "for the scenario to be usable", + .mandatory = FALSE, + .types = "int", + .possible_variables = NULL, + .def = "0" + }, + { + .name = "min-video-track", + .description = "Lets the user know the minimum number of video tracks the stream needs to contain\n" + "for the scenario to be usable", + .mandatory = FALSE, + .types = "int", + .possible_variables = NULL, + .def = "0" + }, + { + .name = "duration", + .description = "Lets the user know the time the scenario needs to be fully executed", + .mandatory = FALSE, + .types = "double, int", + .possible_variables = NULL, + .def = "infinite (GST_CLOCK_TIME_NONE)" + }, + { + .name = "pipeline-name", + .description = "The name of the GstPipeline on which the scenario should be executed.\n" + "It has the same effect as setting the pipeline using pipeline_name->scenario_name.", + .mandatory = FALSE, + .types = "string", + .possible_variables = NULL, + .def = "NULL" + }, + {NULL} + }), + "Allows to describe the scenario in various ways", + GST_VALIDATE_ACTION_TYPE_CONFIG); + + REGISTER_ACTION_TYPE ("seek", _execute_seek, + ((GstValidateActionParameter []) { + { + .name = "start", + .description = "The starting value of the seek", + .mandatory = TRUE, + .types = "double or string", + .possible_variables = "position: The current position in the stream\n" + "duration: The duration of the stream", + NULL + }, + { + .name = "flags", + .description = "The GstSeekFlags to use", + .mandatory = TRUE, + .types = "string describing the GstSeekFlags to set", + NULL, + }, + { + .name = "rate", + .description = "The rate value of the seek", + .mandatory = FALSE, + .types = "double", + .possible_variables = NULL, + .def = "1.0" + }, + { + .name = "start_type", + .description = "The GstSeekType to use for the start of the seek, in:\n" + " [none, set, end]", + .mandatory = FALSE, + .types = "string", + .possible_variables = NULL, + .def = "set" + }, + { + .name = "stop_type", + .description = "The GstSeekType to use for the stop of the seek, in:\n" + " [none, set, end]", + .mandatory = FALSE, + .types = "string", + .possible_variables = NULL, + .def = "set" + }, + {"stop", "The stop value of the seek", FALSE, "double or ", + "position: The current position in the stream\n" + "duration: The duration of the stream" + "GST_CLOCK_TIME_NONE", + }, + {NULL} + }), + "Seeks into the stream, example of a seek happening when the stream reaches 5 seconds\n" + "or 1 eighth of its duration and seeks at 10sec or 2 eighth of its duration:\n" + " seek, playback-time=\"min(5.0, (duration/8))\", start=\"min(10, 2*(duration/8))\", flags=accurate+flush", + GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK + ); + + REGISTER_ACTION_TYPE ("pause", _execute_pause, + ((GstValidateActionParameter []) { + { + .name = "duration", + .description = "The duration during which the stream will be paused", + .mandatory = FALSE, + .types = "double", + .possible_variables = NULL, + .def = "0.0", + }, + {NULL} + }), + "Sets pipeline to PAUSED. You can add a 'duration'\n" + "parametter so the pipeline goes back to playing after that duration\n" + "(in second)", + GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK & GST_VALIDATE_ACTION_TYPE_ASYNC); + + REGISTER_ACTION_TYPE ("play", _execute_play, NULL, + "Sets the pipeline state to PLAYING", GST_VALIDATE_ACTION_TYPE_NONE); + + REGISTER_ACTION_TYPE ("stop", _execute_stop, NULL, + "Stops the execution of the scenario. It will post a 'request-state'" + " message on the bus with NULL as a requested state " + " and the application is responsible for stopping itself." + " if you override that action type, make sure to link up.", + GST_VALIDATE_ACTION_TYPE_NO_EXECUTION_NOT_FATAL); + + REGISTER_ACTION_TYPE ("eos", _execute_eos, NULL, + "Sends an EOS event to the pipeline", + GST_VALIDATE_ACTION_TYPE_NO_EXECUTION_NOT_FATAL); + + REGISTER_ACTION_TYPE ("switch-track", _execute_switch_track, + ((GstValidateActionParameter []) { + { + .name = "type", + .description = "Selects which track type to change (can be 'audio', 'video'," + " or 'text').", + .mandatory = FALSE, + .types = "string", + .possible_variables = NULL, + .def = "audio", + }, + { + .name = "index", + .description = "Selects which track of this type to use: it can be either a number,\n" + "which will be the Nth track of the given type, or a number with a '+' or\n" + "'-' prefix, which means a relative change (eg, '+1' means 'next track',\n" + "'-1' means 'previous track')", + .mandatory = FALSE, + .types = "string: to switch track relatively\n" + "int: To use the actual index to use", + .possible_variables = NULL, + .def = "+1", + }, + {NULL} + }), + "The 'switch-track' command can be used to switch tracks.\n" + , GST_VALIDATE_ACTION_TYPE_NONE); + + REGISTER_ACTION_TYPE ("wait", _execute_wait, + ((GstValidateActionParameter []) { + { + .name = "duration", + .description = "the duration while no other action will be executed", + .mandatory = FALSE, + NULL}, + { + .name = "target-element-name", + .description = "The name of the GstElement to wait @signal-name on.", + .mandatory = FALSE, + .types = "string" + }, + { + .name = "signal-name", + .description = "The name of the signal to wait for on @target-element-name", + .mandatory = FALSE, + .types = "string", + NULL + }, + { + .name = "message-type", + .description = "The name of the message type to wait for (on @target-element-name" + " if specified)", + .mandatory = FALSE, + .types = "string", + NULL + }, + {NULL} + }), + "Waits for signal 'signal-name', message 'message-type', or during 'duration' seconds", + GST_VALIDATE_ACTION_TYPE_DOESNT_NEED_PIPELINE); + + REGISTER_ACTION_TYPE ("dot-pipeline", _execute_dot_pipeline, NULL, + "Dots the pipeline (the 'name' property will be used in the dot filename).\n" + "For more information have a look at the GST_DEBUG_BIN_TO_DOT_FILE documentation.\n" + "Note that the GST_DEBUG_DUMP_DOT_DIR env variable needs to be set\n", + GST_VALIDATE_ACTION_TYPE_NONE); + + REGISTER_ACTION_TYPE ("set-feature-rank", _set_rank, + ((GstValidateActionParameter []) { + { + .name = "feature-name", + .description = "The name of a GstFeature", + .mandatory = TRUE, + .types = "string", + NULL}, + { + .name = "rank", + .description = "The GstRank to set on @feature-name", + .mandatory = TRUE, + .types = "string, int", + NULL}, + {NULL} + }), + "Changes the ranking of a particular plugin feature", + GST_VALIDATE_ACTION_TYPE_CONFIG); + + REGISTER_ACTION_TYPE ("set-state", _execute_set_state, + ((GstValidateActionParameter []) { + { + .name = "state", + .description = "A GstState as a string, should be in: \n" + " * ['null', 'ready', 'paused', 'playing']", + .mandatory = TRUE, + .types = "string", + }, + {NULL} + }), + "Changes the state of the pipeline to any GstState", + GST_VALIDATE_ACTION_TYPE_ASYNC & GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK); + + REGISTER_ACTION_TYPE ("set-property", _execute_set_property, + ((GstValidateActionParameter []) { + /* Either 'target-element-name' or 'target-element-klass' needs to be + * defined */ + { + .name = "target-element-name", + .description = "The name of the GstElement to set a property on", + .mandatory = FALSE, + .types = "string", + NULL + }, + { + .name = "target-element-klass", + .description = "The klass of the GstElements to set a property on", + .mandatory = FALSE, + .types = "string", + NULL + }, + { + .name = "property-name", + .description = "The name of the property to set on @target-element-name", + .mandatory = TRUE, + .types = "string", + NULL + }, + { + .name = "property-value", + .description = "The value of @property-name to be set on the element", + .mandatory = TRUE, + .types = "The same type of @property-name", + NULL + }, + {NULL} + }), + "Sets a property of any element in the pipeline", + GST_VALIDATE_ACTION_TYPE_CAN_EXECUTE_ON_ADDITION | + GST_VALIDATE_ACTION_TYPE_CAN_BE_OPTIONAL); + + REGISTER_ACTION_TYPE ("set-debug-threshold", + _execute_set_debug_threshold, + ((GstValidateActionParameter []) + { + { + .name = "debug-threshold", + .description = "String defining debug threshold\n" + "See gst_debug_set_threshold_from_string", + .mandatory = TRUE, + .types = "string"}, + {NULL} + }), + "Sets the debug level to be used, same format as\n" + "setting the GST_DEBUG env variable", + GST_VALIDATE_ACTION_TYPE_NONE); + + REGISTER_ACTION_TYPE ("emit-signal", _execute_emit_signal, + ((GstValidateActionParameter []) + { + { + .name = "target-element-name", + .description = "The name of the GstElement to emit a signal on", + .mandatory = TRUE, + .types = "string" + }, + { + .name = "signal-name", + .description = "The name of the signal to emit on @target-element-name", + .mandatory = TRUE, + .types = "string", + NULL + }, + {NULL} + }), + "Emits a signal to an element in the pipeline", + GST_VALIDATE_ACTION_TYPE_NONE); + + REGISTER_ACTION_TYPE ("disable-plugin", _execute_disable_plugin, + ((GstValidateActionParameter []) + { + { + .name = "plugin-name", + .description = "The name of the GstPlugin to disable", + .mandatory = TRUE, + .types = "string" + }, + { + .name = "as-config", + .description = "Execute action as a config action (meaning when loading the scenario)", + .mandatory = FALSE, + .types = "boolean", + .def = "false" + }, + {NULL} + }), + "Disables a GstPlugin", + GST_VALIDATE_ACTION_TYPE_NONE); + /* *INDENT-ON* */ + +} diff --git a/validate/gst/validate/gst-validate-scenario.h b/validate/gst/validate/gst-validate-scenario.h new file mode 100644 index 0000000..c4a7f80 --- /dev/null +++ b/validate/gst/validate/gst-validate-scenario.h @@ -0,0 +1,306 @@ +/* GStreamer + * Copyright (C) 2013 Thibault Saunier + * + * gst-validate-runner.c - Validate Runner class + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_VALIDATE_SCENARIO_H__ +#define __GST_VALIDATE_SCENARIO_H__ + +#include +#include + +#include "gst-validate-types.h" +#include + +G_BEGIN_DECLS + +#define GST_TYPE_VALIDATE_SCENARIO (gst_validate_scenario_get_type ()) +#define GST_VALIDATE_SCENARIO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_VALIDATE_SCENARIO, GstValidateScenario)) +#define GST_VALIDATE_SCENARIO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_VALIDATE_SCENARIO, GstValidateScenarioClass)) +#define GST_IS_VALIDATE_SCENARIO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_VALIDATE_SCENARIO)) +#define GST_IS_VALIDATE_SCENARIO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_VALIDATE_SCENARIO)) +#define GST_VALIDATE_SCENARIO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_VALIDATE_SCENARIO, GstValidateScenarioClass)) + +typedef struct _GstValidateScenarioPrivate GstValidateScenarioPrivate; +typedef struct _GstValidateActionParameter GstValidateActionParameter; + +GST_EXPORT GType _gst_validate_action_type; + +typedef enum +{ + GST_VALIDATE_EXECUTE_ACTION_ERROR, + GST_VALIDATE_EXECUTE_ACTION_OK, + GST_VALIDATE_EXECUTE_ACTION_ASYNC, + GST_VALIDATE_EXECUTE_ACTION_INTERLACED, + GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED +} GstValidateActionReturn; + +/* TODO 2.0 -- Make it an actual enum type */ +#define GstValidateExecuteActionReturn gint + +/** + * GstValidateExecuteAction: + * @scenario: The #GstValidateScenario from which the @action is executed + * @action: The #GstValidateAction being executed + * + * A function that executes a #GstValidateAction + * + * Returns: a #GstValidateExecuteActionReturn + */ +typedef GstValidateExecuteActionReturn (*GstValidateExecuteAction) (GstValidateScenario * scenario, GstValidateAction * action); + +/** + * GstValidatePrepareAction: + * @action: The #GstValidateAction to prepare before execution + * + * A function that prepares @action so it can be executed right after. + * Most of the time that function is used to parse and set field with + * equations in the action structure. + * + * Returns: a %TRUE if the action could be prepared and is ready to be run + * %FALSE otherwise + */ +typedef gboolean (*GstValidatePrepareAction) (GstValidateAction * action); + + +typedef struct _GstValidateActionPrivate GstValidateActionPrivate; + +/** + * GstValidateAction: + * @type: The type of the #GstValidateAction, which is the name of the + * GstValidateActionType registered with + * #gst_validate_register_action_type + * @name: The name of the action, set from the user in the scenario + * @structure: the #GstStructure defining the action + * + * The GstValidateAction defined to be executed as part of a scenario + */ +struct _GstValidateAction +{ + GstMiniObject mini_object; + + /*< public > */ + const gchar *type; + const gchar *name; + GstStructure *structure; + GstValidateScenario *scenario; + + /* < private > */ + guint action_number; + gint repeat; + GstClockTime playback_time; + + GstValidateActionPrivate *priv; + + gpointer _gst_reserved[GST_PADDING_LARGE - 2]; /* ->scenario + ->priv */ +}; + +void gst_validate_action_set_done (GstValidateAction *action); + +#define GST_TYPE_VALIDATE_ACTION (gst_validate_action_get_type ()) +#define GST_IS_VALIDATE_ACTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_VALIDATE_ACTION)) +#define GST_VALIDATE_ACTION_GET_TYPE(obj) ((GstValidateActionType*)gst_validate_get_action_type(((GstValidateAction*)obj)->type)) +GType gst_validate_action_get_type (void); + +/** + * GstValidateActionTypeFlags: + * @GST_VALIDATE_ACTION_TYPE_NONE: No special flag + * @GST_VALIDATE_ACTION_TYPE_CONFIG: The action is a config + * @GST_VALIDATE_ACTION_TYPE_ASYNC: The action can be executed ASYNC + * @GST_VALIDATE_ACTION_TYPE_INTERLACED: The action will be executed async + * but without blocking further actions + * to be executed + * @GST_VALIDATE_ACTION_TYPE_CAN_EXECUTE_ON_ADDITION: The action will be executed on 'element-added' + * for a particular element type if no playback-time + * is specified + * @GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK: The pipeline will need to be synchronized with the clock + * for that action type to be used. + * @GST_VALIDATE_ACTION_TYPE_NO_EXECUTION_NOT_FATAL: Do not concider the non execution of the action + * as a fatal error. + * @GST_VALIDATE_ACTION_TYPE_CAN_BE_OPTIONAL: The action can use the 'optional' keyword. Such action + * instances will have the #GST_VALIDATE_ACTION_TYPE_NO_EXECUTION_NOT_FATAL + * flag set and won't be considered as fatal if they fail. + */ +typedef enum +{ + GST_VALIDATE_ACTION_TYPE_NONE = 0, + GST_VALIDATE_ACTION_TYPE_CONFIG = 1 << 1, + GST_VALIDATE_ACTION_TYPE_ASYNC = 1 << 2, + GST_VALIDATE_ACTION_TYPE_INTERLACED = 1 << 3, + GST_VALIDATE_ACTION_TYPE_CAN_EXECUTE_ON_ADDITION = 1 << 4, + GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK = 1 << 5, + GST_VALIDATE_ACTION_TYPE_NO_EXECUTION_NOT_FATAL = 1 << 6, + GST_VALIDATE_ACTION_TYPE_CAN_BE_OPTIONAL = 1 << 7, + GST_VALIDATE_ACTION_TYPE_DOESNT_NEED_PIPELINE = 1 << 8, +} GstValidateActionTypeFlags; + +/** + * @name: The name of the new action type to add + * @implementer_namespace: The namespace of the implementer of the action type + * @execute: (virtual do_execute): The function to be called to execute the action + * @parameters: (allow-none) (array zero-terminated=1) (element-type GstValidate.ActionParameter): The #GstValidateActionParameter usable as parameter of the type + * @description: A description of the new type + * @flags: The flags of the action type + */ +struct _GstValidateActionType +{ + GstMiniObject mini_object; + + gchar *name; + gchar *implementer_namespace; + + GstValidatePrepareAction prepare; + GstValidateExecuteAction execute; + + GstValidateActionParameter *parameters; + + gchar *description; + GstValidateActionTypeFlags flags; + + GstRank rank; + + GstValidateActionType *overriden_type; + + /*< private >*/ + gpointer _gst_reserved[GST_PADDING_LARGE - sizeof (GstRank) - 1]; +}; + +#define GST_TYPE_VALIDATE_ACTION_TYPE (gst_validate_action_type_get_type ()) +#define GST_IS_VALIDATE_ACTION_TYPE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_VALIDATE_ACTION_TYPE)) +#define GST_VALIDATE_ACTION_TYPE(obj) ((GstValidateActionType*) obj) +GType gst_validate_action_type_get_type (void); + +gboolean gst_validate_print_action_types (const gchar ** wanted_types, gint num_wanted_types); + +/** + * GstValidateActionParameter: + * @name: The name of the parameter + * @description: The description of the parameter + * @mandatory: Whether the parameter is mandatory for + * a specific action type + * @types: The types the parameter can take described as a + * string. It can be precisely describing how the typing works + * using '\n' between the various acceptable types. + * @possible_variables: The name of the variables that can be + * used to compute the value of the parameter. + * For example for the start value of a seek + * action, we will accept to take 'duration' + * which will be replace by the total duration + * of the stream on which the action is executed. + * @def: The default value of a parametter as a string, should be %NULL + * for mandatory streams. + */ +struct _GstValidateActionParameter +{ + const gchar *name; + const gchar *description; + gboolean mandatory; + const gchar *types; + const gchar *possible_variables; + const gchar *def; + + /*< private >*/ + gpointer _gst_reserved[GST_PADDING]; +}; + +struct _GstValidateScenarioClass +{ + GObjectClass parent_class; + + /*< public >*/ + /*< private >*/ + gpointer _gst_reserved[GST_PADDING]; +}; + +/** + * GstValidateScenario: + * @pipeline: The #GstPipeline on which the scenario is being executed. + */ +struct _GstValidateScenario +{ + GObject parent; + + /*< public >*/ + GstElement *pipeline; + + /*< private >*/ + GstValidateScenarioPrivate *priv; + + gpointer _gst_reserved[GST_PADDING]; +}; + +GType gst_validate_scenario_get_type (void); + +GstValidateScenario * gst_validate_scenario_factory_create (GstValidateRunner *runner, + GstElement *pipeline, + const gchar *scenario_name); +gboolean +gst_validate_list_scenarios (gchar **scenarios, + gint num_scenarios, + gchar * output_file); + +GstValidateActionType * +gst_validate_get_action_type (const gchar *type_name); + +GstValidateActionType * +gst_validate_register_action_type (const gchar *type_name, + const gchar *implementer_namespace, + GstValidateExecuteAction function, + GstValidateActionParameter * parameters, + const gchar *description, + GstValidateActionTypeFlags flags); + +void +gst_validate_action_type_set_prepare_function (GstValidateActionType *type, + GstValidatePrepareAction prepare_action); + +GstValidateActionType * +gst_validate_register_action_type_dynamic (GstPlugin *plugin, + const gchar * type_name, + GstRank rank, + GstValidateExecuteAction function, + GstValidateActionParameter * parameters, + const gchar * description, + GstValidateActionTypeFlags flags); + + +gboolean gst_validate_action_get_clocktime (GstValidateScenario * scenario, + GstValidateAction *action, + const gchar * name, + GstClockTime * retval); + +gboolean gst_validate_scenario_execute_seek (GstValidateScenario *scenario, + GstValidateAction *action, + gdouble rate, + GstFormat format, + GstSeekFlags flags, + GstSeekType start_type, + GstClockTime start, + GstSeekType stop_type, + GstClockTime stop); + +GList * +gst_validate_scenario_get_actions (GstValidateScenario *scenario); +GstValidateExecuteActionReturn +gst_validate_execute_action (GstValidateActionType * action_type, + GstValidateAction * action); + +G_END_DECLS + +#endif /* __GST_VALIDATE_SCENARIOS__ */ diff --git a/validate/gst/validate/gst-validate-types.h b/validate/gst/validate/gst-validate-types.h new file mode 100644 index 0000000..6ba6c20 --- /dev/null +++ b/validate/gst/validate/gst-validate-types.h @@ -0,0 +1,30 @@ +/* GStreamer + * + * Copyright (C) 2015 Thibault Saunier + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_VALIDATE_TYPES_H__ +#define __GST_VALIDATE_TYPES_H__ + +typedef struct _GstValidateScenario GstValidateScenario; +typedef struct _GstValidateScenarioClass GstValidateScenarioClass; + +typedef struct _GstValidateAction GstValidateAction; +typedef struct _GstValidateActionType GstValidateActionType; + +#endif /* __GST_VALIDATE_TYPES_ */ diff --git a/validate/gst/validate/gst-validate-utils.c b/validate/gst/validate/gst-validate-utils.c new file mode 100644 index 0000000..e948a10 --- /dev/null +++ b/validate/gst/validate/gst-validate-utils.c @@ -0,0 +1,736 @@ +/* GStreamer + * + * Copyright (C) 2013 Thibault Saunier + * + * gst-validate-utils.c - Some utility functions + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include + +#include "gst-validate-utils.h" +#include + +#define PARSER_BOOLEAN_EQUALITY_THRESHOLD (1e-10) +#define PARSER_MAX_TOKEN_SIZE 256 +#define PARSER_MAX_ARGUMENT_COUNT 10 + +static GRegex *_clean_structs_lines = NULL; + +typedef struct +{ + const gchar *str; + gint len; + gint pos; + jmp_buf err_jmp_buf; + const gchar *error; + void *user_data; + ParseVariableFunc variable_func; +} MathParser; + +static gdouble _read_power (MathParser * parser); + +static void +_error (MathParser * parser, const gchar * err) +{ + parser->error = err; + longjmp (parser->err_jmp_buf, 1); +} + +static gchar +_peek (MathParser * parser) +{ + if (parser->pos < parser->len) + return parser->str[parser->pos]; + _error (parser, "Tried to read past end of string!"); + return '\0'; +} + +static gchar +_peek_n (MathParser * parser, gint n) +{ + if (parser->pos + n < parser->len) + return parser->str[parser->pos + n]; + _error (parser, "Tried to read past end of string!"); + return '\0'; +} + +static gchar +_next (MathParser * parser) +{ + if (parser->pos < parser->len) + return parser->str[parser->pos++]; + _error (parser, "Tried to read past end of string!"); + return '\0'; +} + +static gdouble +_read_double (MathParser * parser) +{ + gchar c, token[PARSER_MAX_TOKEN_SIZE]; + gint pos = 0; + gdouble val = 0.0; + + c = _peek (parser); + if (c == '+' || c == '-') + token[pos++] = _next (parser); + + while (isdigit (_peek (parser))) + token[pos++] = _next (parser); + + c = _peek (parser); + if (c == '.') + token[pos++] = _next (parser); + + while (isdigit (_peek (parser))) + token[pos++] = _next (parser); + + c = _peek (parser); + if (c == 'e' || c == 'E') { + token[pos++] = _next (parser); + + c = _peek (parser); + if (c == '+' || c == '-') { + token[pos++] = _next (parser); + } + } + + while (isdigit (_peek (parser))) + token[pos++] = _next (parser); + + token[pos] = '\0'; + + if (pos == 0 || sscanf (token, "%lf", &val) != 1) + _error (parser, "Failed to read real number"); + + return val; +} + +static gdouble +_read_term (MathParser * parser) +{ + gdouble v0; + gchar c; + + v0 = _read_power (parser); + c = _peek (parser); + + while (c == '*' || c == '/') { + _next (parser); + if (c == '*') { + v0 *= _read_power (parser); + } else if (c == '/') { + v0 /= _read_power (parser); + } + c = _peek (parser); + } + return v0; +} + +static gdouble +_read_expr (MathParser * parser) +{ + gdouble v0 = 0.0; + gchar c; + + c = _peek (parser); + if (c == '+' || c == '-') { + _next (parser); + if (c == '+') + v0 += _read_term (parser); + else if (c == '-') + v0 -= _read_term (parser); + } else { + v0 = _read_term (parser); + } + + c = _peek (parser); + while (c == '+' || c == '-') { + _next (parser); + if (c == '+') { + v0 += _read_term (parser); + } else if (c == '-') { + v0 -= _read_term (parser); + } + + c = _peek (parser); + } + + return v0; +} + +static gdouble +_read_boolean_comparison (MathParser * parser) +{ + gchar c, oper[] = { '\0', '\0', '\0' }; + gdouble v0, v1; + + + v0 = _read_expr (parser); + c = _peek (parser); + if (c == '>' || c == '<') { + oper[0] = _next (parser); + c = _peek (parser); + if (c == '=') + oper[1] = _next (parser); + + + v1 = _read_expr (parser); + + if (g_strcmp0 (oper, "<") == 0) { + v0 = (v0 < v1) ? 1.0 : 0.0; + } else if (g_strcmp0 (oper, ">") == 0) { + v0 = (v0 > v1) ? 1.0 : 0.0; + } else if (g_strcmp0 (oper, "<=") == 0) { + v0 = (v0 <= v1) ? 1.0 : 0.0; + } else if (g_strcmp0 (oper, ">=") == 0) { + v0 = (v0 >= v1) ? 1.0 : 0.0; + } else { + _error (parser, "Unknown operation!"); + } + } + return v0; +} + +static gdouble +_read_boolean_equality (MathParser * parser) +{ + gchar c, oper[] = { '\0', '\0', '\0' }; + gdouble v0, v1; + + v0 = _read_boolean_comparison (parser); + c = _peek (parser); + if (c == '=' || c == '!') { + if (c == '!') { + if (_peek_n (parser, 1) == '=') { + oper[0] = _next (parser); + oper[1] = _next (parser); + } else { + return v0; + } + } else { + oper[0] = _next (parser); + c = _peek (parser); + if (c != '=') + _error (parser, "Expected a '=' for boolean '==' operator!"); + oper[1] = _next (parser); + } + v1 = _read_boolean_comparison (parser); + if (g_strcmp0 (oper, "==") == 0) { + v0 = (fabs (v0 - v1) < PARSER_BOOLEAN_EQUALITY_THRESHOLD) ? 1.0 : 0.0; + } else if (g_strcmp0 (oper, "!=") == 0) { + v0 = (fabs (v0 - v1) > PARSER_BOOLEAN_EQUALITY_THRESHOLD) ? 1.0 : 0.0; + } else { + _error (parser, "Unknown operation!"); + } + } + return v0; +} + +static gdouble +_read_boolean_and (MathParser * parser) +{ + gchar c; + gdouble v0, v1; + + v0 = _read_boolean_equality (parser); + + c = _peek (parser); + while (c == '&') { + _next (parser); + + c = _peek (parser); + if (c != '&') + _error (parser, "Expected '&' to follow '&' in logical and operation!"); + _next (parser); + + v1 = _read_boolean_equality (parser); + v0 = (fabs (v0) >= PARSER_BOOLEAN_EQUALITY_THRESHOLD + && fabs (v1) >= PARSER_BOOLEAN_EQUALITY_THRESHOLD) ? 1.0 : 0.0; + + c = _peek (parser); + } + + return v0; +} + +static gdouble +_read_boolean_or (MathParser * parser) +{ + gchar c; + gdouble v0, v1; + + v0 = _read_boolean_and (parser); + + c = _peek (parser); + while (c == '|') { + _next (parser); + c = _peek (parser); + if (c != '|') + _error (parser, "Expected '|' to follow '|' in logical or operation!"); + _next (parser); + v1 = _read_boolean_and (parser); + v0 = (fabs (v0) >= PARSER_BOOLEAN_EQUALITY_THRESHOLD + || fabs (v1) >= PARSER_BOOLEAN_EQUALITY_THRESHOLD) ? 1.0 : 0.0; + c = _peek (parser); + } + + return v0; +} + +static gboolean +_init (MathParser * parser, const gchar * str, + ParseVariableFunc variable_func, void *user_data) +{ + parser->str = str; + parser->len = strlen (str) + 1; + parser->pos = 0; + parser->error = NULL; + parser->user_data = user_data; + parser->variable_func = variable_func; + + return TRUE; +} + +static gdouble +_parse (MathParser * parser) +{ + gdouble result = 0.0; + + if (!setjmp (parser->err_jmp_buf)) { + result = _read_expr (parser); + if (parser->pos < parser->len - 1) { + _error (parser, + "Failed to reach end of input expression, likely malformed input"); + } else + return result; + } + + return -1.0; +} + +static gdouble +_read_argument (MathParser * parser) +{ + gchar c; + gdouble val; + + val = _read_expr (parser); + c = _peek (parser); + if (c == ',') + _next (parser); + + return val; +} + +static gdouble +_read_builtin (MathParser * parser) +{ + gdouble v0 = 0.0, v1 = 0.0; + gchar c, token[PARSER_MAX_TOKEN_SIZE]; + gint pos = 0; + + c = _peek (parser); + if (isalpha (c) || c == '_') { + while (isalpha (c) || isdigit (c) || c == '_') { + token[pos++] = _next (parser); + c = _peek (parser); + } + token[pos] = '\0'; + + if (_peek (parser) == '(') { + _next (parser); + if (g_strcmp0 (token, "min") == 0) { + v0 = _read_argument (parser); + v1 = _read_argument (parser); + v0 = MIN (v0, v1); + } else if (g_strcmp0 (token, "max") == 0) { + v0 = _read_argument (parser); + v1 = _read_argument (parser); + v0 = MAX (v0, v1); + } else { + _error (parser, "Tried to call unknown built-in function!"); + } + + if (_next (parser) != ')') + _error (parser, "Expected ')' in built-in call!"); + } else { + if (parser->variable_func != NULL + && parser->variable_func (token, &v1, parser->user_data)) { + v0 = v1; + } else { + _error (parser, "Could not look up value for variable %s!"); + } + } + } else { + v0 = _read_double (parser); + } + + return v0; +} + +static gdouble +_read_parenthesis (MathParser * parser) +{ + gdouble val; + + if (_peek (parser) == '(') { + _next (parser); + val = _read_boolean_or (parser); + if (_peek (parser) != ')') + _error (parser, "Expected ')'!"); + _next (parser); + } else { + val = _read_builtin (parser); + } + + return val; +} + +static gdouble +_read_unary (MathParser * parser) +{ + gchar c; + gdouble v0 = 0.0; + + c = _peek (parser); + if (c == '!') { + _error (parser, "Expected '+' or '-' for unary expression, got '!'"); + } else if (c == '-') { + _next (parser); + v0 = -_read_parenthesis (parser); + } else if (c == '+') { + _next (parser); + v0 = _read_parenthesis (parser); + } else { + v0 = _read_parenthesis (parser); + } + return v0; +} + +static gdouble +_read_power (MathParser * parser) +{ + gdouble v0, v1 = 1.0, s = 1.0; + + v0 = _read_unary (parser); + + while (_peek (parser) == '^') { + _next (parser); + if (_peek (parser) == '-') { + _next (parser); + s = -1.0; + } + v1 = s * _read_power (parser); + v0 = pow (v0, v1); + } + + return v0; +} + +gdouble +gst_validate_utils_parse_expression (const gchar * expr, + ParseVariableFunc variable_func, gpointer user_data, gchar ** error) +{ + gdouble val; + MathParser parser; + gchar **spl = g_strsplit (expr, " ", -1); + gchar *expr_nospace = g_strjoinv ("", spl); + + _init (&parser, expr_nospace, variable_func, user_data); + val = _parse (&parser); + g_strfreev (spl); + g_free (expr_nospace); + + if (error) { + if (parser.error) + *error = g_strdup (parser.error); + else + *error = NULL; + } + return val; +} + +/** + * gst_validate_utils_flags_from_str: + * @type: The #GType of the flags we are trying to retrieve the flags from + * @str_flags: The string representation of the value + * + * Returns: The flags set in @str_flags + */ +guint +gst_validate_utils_flags_from_str (GType type, const gchar * str_flags) +{ + guint i; + gint flags = 0; + GFlagsClass *class = g_type_class_ref (type); + + for (i = 0; i < class->n_values; i++) { + if (class->values[i].value_nick == NULL) + continue; + + if (g_strrstr (str_flags, class->values[i].value_nick)) { + flags |= class->values[i].value; + } + } + g_type_class_unref (class); + + return flags; +} + +/** + * gst_validate_utils_enum_from_str: + * @type: The #GType of the enum we are trying to retrieve the enum value from + * @str_enum: The string representation of the value + * @enum_value: (out): The value of the enum + * + * Returns: %TRUE on success %FALSE otherwise + */ +gboolean +gst_validate_utils_enum_from_str (GType type, const gchar * str_enum, + guint * enum_value) +{ + guint i; + GEnumClass *class = g_type_class_ref (type); + gboolean ret = FALSE; + + for (i = 0; i < class->n_values; i++) { + if (g_strrstr (str_enum, class->values[i].value_nick)) { + *enum_value = class->values[i].value; + ret = TRUE; + } + } + + g_type_class_unref (class); + + return ret; +} + +/* Parse file that contains a list of GStructures */ +static gchar ** +_file_get_lines (GFile * file) +{ + gsize size; + + GError *err = NULL; + gchar *content = NULL, *escaped_content = NULL, **lines = NULL; + + /* TODO Handle GCancellable */ + if (!g_file_load_contents (file, NULL, &content, &size, NULL, &err)) { + GST_WARNING ("Failed to load contents: %d %s", err->code, err->message); + g_error_free (err); + return NULL; + } + if (g_strcmp0 (content, "") == 0) { + g_free (content); + return NULL; + } + + if (_clean_structs_lines == NULL) + _clean_structs_lines = + g_regex_new ("\\\\\n|#.*\n", G_REGEX_CASELESS, 0, NULL); + + escaped_content = + g_regex_replace (_clean_structs_lines, content, -1, 0, "", 0, NULL); + g_free (content); + + lines = g_strsplit (escaped_content, "\n", 0); + g_free (escaped_content); + + return lines; +} + +static gchar ** +_get_lines (const gchar * scenario_file) +{ + GFile *file = NULL; + gchar **lines = NULL; + + GST_DEBUG ("Trying to load %s", scenario_file); + if ((file = g_file_new_for_path (scenario_file)) == NULL) { + GST_WARNING ("%s wrong uri", scenario_file); + return NULL; + } + + lines = _file_get_lines (file); + + g_object_unref (file); + + return lines; +} + +/* Returns: (transfer full): a #GList of #GstStructure */ +static GList * +_lines_get_strutures (gchar ** lines) +{ + gint i; + GList *structures = NULL; + + for (i = 0; lines[i]; i++) { + GstStructure *structure; + + if (g_strcmp0 (lines[i], "") == 0) + continue; + + structure = gst_structure_from_string (lines[i], NULL); + if (structure == NULL) { + GST_ERROR ("Could not parse action %s", lines[i]); + goto failed; + } + + structures = g_list_append (structures, structure); + } + +done: + if (lines) + g_strfreev (lines); + + return structures; + +failed: + if (structures) + g_list_free_full (structures, (GDestroyNotify) gst_structure_free); + structures = NULL; + + goto done; +} + +GList * +gst_validate_utils_structs_parse_from_filename (const gchar * scenario_file) +{ + gchar **lines; + + lines = _get_lines (scenario_file); + + if (lines == NULL) { + GST_DEBUG ("Got no line for file: %s", scenario_file); + return NULL; + } + + return _lines_get_strutures (lines); +} + +GList * +structs_parse_from_gfile (GFile * scenario_file) +{ + gchar **lines; + + lines = _file_get_lines (scenario_file); + + if (lines == NULL) + return NULL; + + return _lines_get_strutures (lines); +} + +static gboolean +strv_contains (GStrv strv, const gchar * str) +{ + guint i; + + for (i = 0; strv[i] != NULL; i++) + if (g_strcmp0 (strv[i], str) == 0) + return TRUE; + + return FALSE; +} + +gboolean +gst_validate_element_has_klass (GstElement * element, const gchar * klass) +{ + const gchar *tmp; + gchar **a, **b; + gboolean result = FALSE; + guint i; + + tmp = gst_element_class_get_metadata (GST_ELEMENT_GET_CLASS (element), + GST_ELEMENT_METADATA_KLASS); + + a = g_strsplit (klass, "/", -1); + b = g_strsplit (tmp, "/", -1); + + /* All the elements in 'a' have to be in 'b' */ + for (i = 0; a[i] != NULL; i++) + if (!strv_contains (b, a[i])) + goto done; + result = TRUE; + +done: + g_strfreev (a); + g_strfreev (b); + return result; +} + +gboolean +gst_validate_utils_get_clocktime (GstStructure * structure, const gchar * name, + GstClockTime * retval) +{ + gdouble val; + const GValue *gvalue = gst_structure_get_value (structure, name); + + *retval = GST_CLOCK_TIME_NONE; + if (gvalue == NULL) { + return FALSE; + } + + if (G_VALUE_TYPE (gvalue) == GST_TYPE_CLOCK_TIME) { + *retval = g_value_get_uint64 (gvalue); + + return TRUE; + } + + if (G_VALUE_TYPE (gvalue) == G_TYPE_UINT64) { + *retval = g_value_get_uint64 (gvalue); + + return TRUE; + } + + if (G_VALUE_TYPE (gvalue) == G_TYPE_UINT) { + *retval = (GstClockTime) g_value_get_uint (gvalue); + + return TRUE; + } + + if (G_VALUE_TYPE (gvalue) == G_TYPE_INT) { + *retval = (GstClockTime) g_value_get_int (gvalue); + + return TRUE; + } + + if (G_VALUE_TYPE (gvalue) == G_TYPE_INT64) { + *retval = (GstClockTime) g_value_get_int64 (gvalue); + + return TRUE; + } + + if (!gst_structure_get_double (structure, name, &val)) { + return FALSE; + } + + if (val == -1.0) + *retval = GST_CLOCK_TIME_NONE; + else { + *retval = val * GST_SECOND; + *retval = GST_ROUND_UP_4 (*retval); + } + + return TRUE; +} diff --git a/validate/gst/validate/gst-validate-utils.h b/validate/gst/validate/gst-validate-utils.h new file mode 100644 index 0000000..a181be6 --- /dev/null +++ b/validate/gst/validate/gst-validate-utils.h @@ -0,0 +1,51 @@ +/* GStreamer + * + * Copyright (C) 2013 Thibault Saunier + * + * gst-validate-utils.h - Some utility functions + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef EXPRESSION_PARSER_H +#define EXPRESSION_PARSER_H + +#include +#include +#include +#include +#include + +typedef int (*ParseVariableFunc) (const gchar *name, + double *value, gpointer user_data); + +gdouble gst_validate_utils_parse_expression (const gchar *expr, + ParseVariableFunc variable_func, + gpointer user_data, + gchar **error); +guint gst_validate_utils_flags_from_str (GType type, const gchar * str_flags); +gboolean gst_validate_utils_enum_from_str (GType type, + const gchar * str_enum, + guint * enum_value); + +GList * gst_validate_utils_structs_parse_from_filename (const gchar * scenario_file); +GList * structs_parse_from_gfile (GFile * scenario_file); + +gboolean gst_validate_element_has_klass (GstElement * element, const gchar * klass); +gboolean gst_validate_utils_get_clocktime (GstStructure *structure, const gchar * name, + GstClockTime * retval); + +#endif diff --git a/validate/gst/validate/media-descriptor-parser.c b/validate/gst/validate/media-descriptor-parser.c new file mode 100644 index 0000000..ebcd2cd --- /dev/null +++ b/validate/gst/validate/media-descriptor-parser.c @@ -0,0 +1,508 @@ +/** + * Gstreamer + * + * Copyright (c) 2012, Collabora Ltd. + * Author: Thibault Saunier + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "media-descriptor-parser.h" +#include + +G_DEFINE_TYPE (GstMediaDescriptorParser, gst_media_descriptor_parser, + GST_TYPE_MEDIA_DESCRIPTOR); + +enum +{ + PROP_0, + PROP_PATH, + N_PROPERTIES +}; + +struct _GstMediaDescriptorParserPrivate +{ + gchar *xmlpath; + + gboolean in_stream; + gchar *xmlcontent; + GMarkupParseContext *parsecontext; +}; + +/* Private methods and callbacks */ +static gint +compare_frames (FrameNode * frm, FrameNode * frm1) +{ + if (frm->id < frm1->id) + return -1; + + else if (frm->id == frm1->id) + return 0; + + return 1; +} + +static void +deserialize_filenode (FileNode * filenode, + const gchar ** names, const gchar ** values) +{ + gint i; + for (i = 0; names[i] != NULL; i++) { + if (g_strcmp0 (names[i], "uri") == 0) + filenode->uri = g_strdup (values[i]); + else if (g_strcmp0 (names[i], "id") == 0) + filenode->id = g_ascii_strtoull (values[i], NULL, 0); + else if (g_strcmp0 (names[i], "frame-detection") == 0) + filenode->frame_detection = g_ascii_strtoull (values[i], NULL, 0); + else if (g_strcmp0 (names[i], "duration") == 0) + filenode->duration = g_ascii_strtoull (values[i], NULL, 0); + else if (g_strcmp0 (names[i], "seekable") == 0) + filenode->seekable = (gboolean) g_strcmp0 (values[i], "false"); + } +} + +static StreamNode * +deserialize_streamnode (const gchar ** names, const gchar ** values) +{ + gint i; + StreamNode *streamnode = g_slice_new0 (StreamNode); + + for (i = 0; names[i] != NULL; i++) { + if (g_strcmp0 (names[i], "id") == 0) + streamnode->id = g_strdup (values[i]); + else if (g_strcmp0 (names[i], "caps") == 0) + streamnode->caps = gst_caps_from_string (values[i]); + else if (g_strcmp0 (names[i], "padname") == 0) + streamnode->padname = g_strdup (values[i]); + } + + + return streamnode; +} + +static TagsNode * +deserialize_tagsnode (const gchar ** names, const gchar ** values) +{ + TagsNode *tagsnode = g_slice_new0 (TagsNode); + + return tagsnode; +} + +static TagNode * +deserialize_tagnode (const gchar ** names, const gchar ** values) +{ + gint i; + TagNode *tagnode = g_slice_new0 (TagNode); + + for (i = 0; names[i] != NULL; i++) { + if (g_strcmp0 (names[i], "content") == 0) + tagnode->taglist = gst_tag_list_new_from_string (values[i]); + } + + return tagnode; +} + +static FrameNode * +deserialize_framenode (const gchar ** names, const gchar ** values) +{ + gint i; + + FrameNode *framenode = g_slice_new0 (FrameNode); + + for (i = 0; names[i] != NULL; i++) { + if (g_strcmp0 (names[i], "id") == 0) + framenode->id = g_ascii_strtoull (values[i], NULL, 0); + else if (g_strcmp0 (names[i], "offset") == 0) + framenode->offset = g_ascii_strtoull (values[i], NULL, 0); + else if (g_strcmp0 (names[i], "offset-end") == 0) + framenode->offset_end = g_ascii_strtoull (values[i], NULL, 0); + else if (g_strcmp0 (names[i], "duration") == 0) + framenode->duration = g_ascii_strtoull (values[i], NULL, 0); + else if (g_strcmp0 (names[i], "pts") == 0) + framenode->pts = g_ascii_strtoull (values[i], NULL, 0); + else if (g_strcmp0 (names[i], "dts") == 0) + framenode->dts = g_ascii_strtoull (values[i], NULL, 0); + else if (g_strcmp0 (names[i], "checksum") == 0) + framenode->checksum = g_strdup (values[i]); + else if (g_strcmp0 (names[i], "is-keyframe") == 0) { + if (!g_ascii_strcasecmp (values[i], "true")) + framenode->is_keyframe = TRUE; + else + framenode->is_keyframe = FALSE; + } + } + + framenode->buf = gst_buffer_new_wrapped (framenode->checksum, + strlen (framenode->checksum) + 1); + + GST_BUFFER_OFFSET (framenode->buf) = framenode->offset; + GST_BUFFER_OFFSET_END (framenode->buf) = framenode->offset_end; + GST_BUFFER_DURATION (framenode->buf) = framenode->duration; + GST_BUFFER_PTS (framenode->buf) = framenode->pts; + GST_BUFFER_DTS (framenode->buf) = framenode->dts; + + if (framenode->is_keyframe) { + GST_BUFFER_FLAG_UNSET (framenode->buf, GST_BUFFER_FLAG_DELTA_UNIT); + } else { + GST_BUFFER_FLAG_SET (framenode->buf, GST_BUFFER_FLAG_DELTA_UNIT); + } + + return framenode; +} + + +static void +on_end_element_cb (GMarkupParseContext * context, + const gchar * element_name, gpointer user_data, GError ** error) +{ + GstMediaDescriptorParserPrivate *priv = + GST_MEDIA_DESCRIPTOR_PARSER (user_data)->priv; + + if (g_strcmp0 (element_name, "stream") == 0) { + priv->in_stream = FALSE; + } +} + +static void +on_start_element_cb (GMarkupParseContext * context, + const gchar * element_name, const gchar ** attribute_names, + const gchar ** attribute_values, gpointer user_data, GError ** error) +{ + FileNode *filenode = GST_MEDIA_DESCRIPTOR (user_data)->filenode; + + GstMediaDescriptorParserPrivate *priv = + GST_MEDIA_DESCRIPTOR_PARSER (user_data)->priv; + + if (g_strcmp0 (element_name, "file") == 0) { + deserialize_filenode (filenode, attribute_names, attribute_values); + } else if (g_strcmp0 (element_name, "stream") == 0) { + StreamNode *node = + deserialize_streamnode (attribute_names, attribute_values); + priv->in_stream = TRUE; + filenode->streams = g_list_prepend (filenode->streams, node); + } else if (g_strcmp0 (element_name, "frame") == 0) { + StreamNode *streamnode = filenode->streams->data; + + streamnode->cframe = streamnode->frames = + g_list_insert_sorted (streamnode->frames, + deserialize_framenode (attribute_names, attribute_values), + (GCompareFunc) compare_frames); + } else if (g_strcmp0 (element_name, "tags") == 0) { + if (priv->in_stream) { + StreamNode *snode = (StreamNode *) filenode->streams->data; + + snode->tags = deserialize_tagsnode (attribute_names, attribute_values); + } else { + filenode->tags = deserialize_tagsnode (attribute_names, attribute_values); + } + } else if (g_strcmp0 (element_name, "tag") == 0) { + TagsNode *tagsnode; + + if (priv->in_stream) { + StreamNode *snode = (StreamNode *) filenode->streams->data; + tagsnode = snode->tags; + } else { + tagsnode = filenode->tags; + } + + tagsnode->tags = g_list_prepend (tagsnode->tags, + deserialize_tagnode (attribute_names, attribute_values)); + } +} + +static void +on_error_cb (GMarkupParseContext * context, GError * error, gpointer user_data) +{ + GST_ERROR ("Error parsing file: %s", error->message); +} + +static const GMarkupParser content_parser = { + on_start_element_cb, + on_end_element_cb, + NULL, + NULL, + &on_error_cb +}; + +static gboolean +_set_content (GstMediaDescriptorParser * parser, + const gchar * content, gsize size, GError ** error) +{ + GError *err = NULL; + GstMediaDescriptorParserPrivate *priv = parser->priv; + + priv->parsecontext = g_markup_parse_context_new (&content_parser, + G_MARKUP_TREAT_CDATA_AS_TEXT, parser, NULL); + + if (g_markup_parse_context_parse (priv->parsecontext, content, + size, &err) == FALSE) + goto failed; + + return TRUE; + +failed: + g_propagate_error (error, err); + return FALSE; +} + +static gboolean +set_xml_path (GstMediaDescriptorParser * parser, const gchar * path, + GError ** error) +{ + gsize xmlsize; + gchar *content; + GError *err = NULL; + GstMediaDescriptorParserPrivate *priv = parser->priv; + gboolean result; + + if (!g_file_get_contents (path, &content, &xmlsize, &err)) + goto failed; + + priv->xmlpath = g_strdup (path); + + result = _set_content (parser, content, xmlsize, error); + g_free (content); + return result; + +failed: + g_propagate_error (error, err); + return FALSE; +} + +/* GObject standard vmethods */ +static void +dispose (GstMediaDescriptorParser * parser) +{ + G_OBJECT_CLASS (gst_media_descriptor_parser_parent_class)->dispose (G_OBJECT + (parser)); +} + +static void +finalize (GstMediaDescriptorParser * parser) +{ + GstMediaDescriptorParserPrivate *priv; + + priv = parser->priv; + + g_free (priv->xmlpath); + g_free (priv->xmlcontent); + + if (priv->parsecontext != NULL) + g_markup_parse_context_free (priv->parsecontext); + + G_OBJECT_CLASS (gst_media_descriptor_parser_parent_class)->finalize (G_OBJECT + (parser)); +} + + +static void +get_property (GObject * gobject, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + switch (prop_id) { + default: + g_assert_not_reached (); + } + +} + +static void +set_property (GObject * gobject, guint prop_id, const GValue * value, + GParamSpec * pspec) +{ + switch (prop_id) { + default: + g_assert_not_reached (); + } +} + +static void +gst_media_descriptor_parser_init (GstMediaDescriptorParser * parser) +{ + GstMediaDescriptorParserPrivate *priv; + + parser->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (parser, + GST_TYPE_MEDIA_DESCRIPTOR_PARSER, GstMediaDescriptorParserPrivate); + + priv->xmlpath = NULL; +} + +static void +gst_media_descriptor_parser_class_init (GstMediaDescriptorParserClass * + self_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (self_class); + + g_type_class_add_private (self_class, + sizeof (GstMediaDescriptorParserPrivate)); + object_class->dispose = (void (*)(GObject * object)) dispose; + object_class->finalize = (void (*)(GObject * object)) finalize; + object_class->get_property = get_property; + object_class->set_property = set_property; +} + +/* Public methods */ +GstMediaDescriptorParser * +gst_media_descriptor_parser_new (GstValidateRunner * runner, + const gchar * xmlpath, GError ** error) +{ + GstMediaDescriptorParser *parser; + + parser = g_object_new (GST_TYPE_MEDIA_DESCRIPTOR_PARSER, "validate-runner", + runner, NULL); + + if (set_xml_path (parser, xmlpath, error) == FALSE) { + g_object_unref (parser); + + return NULL; + } + + + return parser; +} + +GstMediaDescriptorParser * +gst_media_descriptor_parser_new_from_xml (GstValidateRunner * runner, + const gchar * xml, GError ** error) +{ + GstMediaDescriptorParser *parser; + + parser = g_object_new (GST_TYPE_MEDIA_DESCRIPTOR_PARSER, "validate-runner", + runner, NULL); + if (_set_content (parser, xml, strlen (xml) * sizeof (gchar), error) == FALSE) { + g_object_unref (parser); + + return NULL; + } + + + return parser; +} + +gchar * +gst_media_descriptor_parser_get_xml_path (GstMediaDescriptorParser * parser) +{ + g_return_val_if_fail (GST_IS_MEDIA_DESCRIPTOR_PARSER (parser), NULL); + + return g_strdup (parser->priv->xmlpath); +} + +gboolean +gst_media_descriptor_parser_add_stream (GstMediaDescriptorParser * parser, + GstPad * pad) +{ + GList *tmp; + gboolean ret = FALSE; + GstCaps *caps; + + g_return_val_if_fail (GST_IS_MEDIA_DESCRIPTOR_PARSER (parser), FALSE); + g_return_val_if_fail (((GstMediaDescriptor *) parser)->filenode, FALSE); + + caps = gst_pad_query_caps (pad, NULL); + for (tmp = ((GstMediaDescriptor *) parser)->filenode->streams; tmp; + tmp = tmp->next) { + StreamNode *streamnode = (StreamNode *) tmp->data; + + if (streamnode->pad == NULL && gst_caps_is_equal (streamnode->caps, caps)) { + ret = TRUE; + streamnode->pad = gst_object_ref (pad); + + goto done; + } + } + +done: + if (caps != NULL) + gst_caps_unref (caps); + + return ret; +} + +gboolean +gst_media_descriptor_parser_all_stream_found (GstMediaDescriptorParser * parser) +{ + GList *tmp; + + g_return_val_if_fail (GST_IS_MEDIA_DESCRIPTOR_PARSER (parser), FALSE); + g_return_val_if_fail (((GstMediaDescriptor *) parser)->filenode, FALSE); + + for (tmp = ((GstMediaDescriptor *) parser)->filenode->streams; tmp; + tmp = tmp->next) { + StreamNode *streamnode = (StreamNode *) tmp->data; + + if (streamnode->pad == NULL) + return FALSE; + + } + + return TRUE; +} + +gboolean +gst_media_descriptor_parser_add_taglist (GstMediaDescriptorParser * parser, + GstTagList * taglist) +{ + GList *tmptag; + TagsNode *tagsnode; + + g_return_val_if_fail (GST_IS_MEDIA_DESCRIPTOR_PARSER (parser), FALSE); + g_return_val_if_fail (((GstMediaDescriptor *) parser)->filenode, FALSE); + g_return_val_if_fail (GST_IS_STRUCTURE (taglist), FALSE); + + tagsnode = ((GstMediaDescriptor *) parser)->filenode->tags; + + for (tmptag = tagsnode->tags; tmptag; tmptag = tmptag->next) { + if (tag_node_compare ((TagNode *) tmptag->data, taglist)) { + GST_DEBUG ("Adding tag %" GST_PTR_FORMAT, taglist); + return TRUE; + } + } + + return FALSE; +} + +gboolean +gst_media_descriptor_parser_all_tags_found (GstMediaDescriptorParser * parser) +{ + GList *tmptag; + TagsNode *tagsnode; + gboolean ret = TRUE; + + g_return_val_if_fail (GST_IS_MEDIA_DESCRIPTOR_PARSER (parser), FALSE); + g_return_val_if_fail (((GstMediaDescriptor *) parser)->filenode, FALSE); + + tagsnode = ((GstMediaDescriptor *) parser)->filenode->tags; + for (tmptag = tagsnode->tags; tmptag; tmptag = tmptag->next) { + gchar *tag = NULL; + + tag = gst_tag_list_to_string (((TagNode *) tmptag->data)->taglist); + if (((TagNode *) tmptag->data)->found == FALSE) { + + if (((TagNode *) tmptag->data)->taglist != NULL) { + GST_DEBUG ("Tag not found %s", tag); + } else { + GST_DEBUG ("Tag not not properly deserialized"); + } + + ret = FALSE; + } + + GST_DEBUG ("Tag properly found found %s", tag); + g_free (tag); + } + + return ret; +} diff --git a/validate/gst/validate/media-descriptor-parser.h b/validate/gst/validate/media-descriptor-parser.h new file mode 100644 index 0000000..4c08119 --- /dev/null +++ b/validate/gst/validate/media-descriptor-parser.h @@ -0,0 +1,75 @@ +/** + * GstValidate + * + * Copyright (c) 2012, Collabora Ltd + * Author: Thibault Saunier + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef GST_MEDIA_DESCRIPTOR_PARSER_h +#define GST_MEDIA_DESCRIPTOR_PARSER_h + +#include +#include +#include +#include "media-descriptor.h" + +G_BEGIN_DECLS + +GType gst_media_descriptor_parser_get_type (void); + +#define GST_TYPE_MEDIA_DESCRIPTOR_PARSER (gst_media_descriptor_parser_get_type ()) +#define GST_MEDIA_DESCRIPTOR_PARSER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_MEDIA_DESCRIPTOR_PARSER, GstMediaDescriptorParser)) +#define GST_MEDIA_DESCRIPTOR_PARSER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_MEDIA_DESCRIPTOR_PARSER, GstMediaDescriptorParserClass)) +#define GST_IS_MEDIA_DESCRIPTOR_PARSER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_MEDIA_DESCRIPTOR_PARSER)) +#define GST_IS_MEDIA_DESCRIPTOR_PARSER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_MEDIA_DESCRIPTOR_PARSER)) +#define GST_MEDIA_DESCRIPTOR_PARSER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_MEDIA_DESCRIPTOR_PARSER, GstMediaDescriptorParserClass)) + +typedef struct _GstMediaDescriptorParserPrivate GstMediaDescriptorParserPrivate; + + +typedef struct { + GstMediaDescriptor parent; + + GstMediaDescriptorParserPrivate *priv; + +} GstMediaDescriptorParser; + +typedef struct { + + GstMediaDescriptorClass parent; + +} GstMediaDescriptorParserClass; + +GstMediaDescriptorParser * gst_media_descriptor_parser_new (GstValidateRunner *runner, + const gchar * xmlpath, + GError **error); +GstMediaDescriptorParser * +gst_media_descriptor_parser_new_from_xml (GstValidateRunner * runner, + const gchar * xml, + GError ** error); +gchar * gst_media_descriptor_parser_get_xml_path (GstMediaDescriptorParser *parser); +gboolean gst_media_descriptor_parser_add_stream (GstMediaDescriptorParser *parser, + GstPad *pad); +gboolean gst_media_descriptor_parser_add_taglist (GstMediaDescriptorParser *parser, + GstTagList *taglist); +gboolean gst_media_descriptor_parser_all_stream_found (GstMediaDescriptorParser *parser); +gboolean gst_media_descriptor_parser_all_tags_found (GstMediaDescriptorParser *parser); + +G_END_DECLS + +#endif /* GST_MEDIA_DESCRIPTOR_PARSER_h */ diff --git a/validate/gst/validate/media-descriptor-writer.c b/validate/gst/validate/media-descriptor-writer.c new file mode 100644 index 0000000..d7aac0c --- /dev/null +++ b/validate/gst/validate/media-descriptor-writer.c @@ -0,0 +1,818 @@ +/** + * Gstreamer + * + * Copyright (c) 2012, Collabora Ltd. + * Author: Thibault Saunier + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include "media-descriptor-writer.h" +#include + +G_DEFINE_TYPE (GstMediaDescriptorWriter, + gst_media_descriptor_writer, GST_TYPE_MEDIA_DESCRIPTOR); + +#define STR_APPEND(arg, nb_white) \ + g_string_append_printf (res, "%*s%s%s", (nb_white), " ", (arg), "\n"); \ + +#define STR_APPEND0(arg) STR_APPEND((arg), 0) +#define STR_APPEND1(arg) STR_APPEND((arg), 2) +#define STR_APPEND2(arg) STR_APPEND((arg), 4) +#define STR_APPEND3(arg) STR_APPEND((arg), 6) +#define STR_APPEND4(arg) STR_APPEND((arg), 8) + + +enum +{ + PROP_0, + PROP_PATH, + N_PROPERTIES +}; + +struct _GstMediaDescriptorWriterPrivate +{ + GstElement *pipeline; + GstCaps *raw_caps; + GMainLoop *loop; + + GList *parsers; +}; + +static void +finalize (GstMediaDescriptorWriter * writer) +{ + if (writer->priv->raw_caps) + gst_caps_unref (writer->priv->raw_caps); + + if (writer->priv->parsers) + gst_plugin_feature_list_free (writer->priv->parsers); + + G_OBJECT_CLASS (gst_media_descriptor_writer_parent_class)->finalize (G_OBJECT + (writer)); +} + +static void +get_property (GObject * gobject, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + switch (prop_id) { + default: + g_assert_not_reached (); + } + +} + +static void +set_property (GObject * gobject, guint prop_id, const GValue * value, + GParamSpec * pspec) +{ + switch (prop_id) { + default: + g_assert_not_reached (); + } +} + +static void +gst_media_descriptor_writer_init (GstMediaDescriptorWriter * writer) +{ + GstMediaDescriptorWriterPrivate *priv; + + + writer->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (writer, + GST_TYPE_MEDIA_DESCRIPTOR_WRITER, GstMediaDescriptorWriterPrivate); + + writer->priv->parsers = + gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_PARSER, + GST_RANK_MARGINAL); +} + +static void + gst_media_descriptor_writer_class_init + (GstMediaDescriptorWriterClass * self_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (self_class); + + g_type_class_add_private (self_class, + sizeof (GstMediaDescriptorWriterPrivate)); + object_class->finalize = (void (*)(GObject * object)) finalize; + object_class->get_property = get_property; + object_class->set_property = set_property; +} + +/* Private methods */ +static gchar * +serialize_filenode (GstMediaDescriptorWriter * writer) +{ + GString *res; + gchar *tmpstr, *caps_str; + GList *tmp, *tmp2; + TagsNode *tagsnode; + FileNode *filenode = ((GstMediaDescriptor *) writer)->filenode; + + tmpstr = g_markup_printf_escaped ("\n", + filenode->duration, filenode->frame_detection, filenode->uri, + filenode->seekable ? "true" : "false"); + + if (filenode->caps) + caps_str = gst_caps_to_string (filenode->caps); + else + caps_str = g_strdup (""); + + res = g_string_new (tmpstr); + g_string_append_printf (res, " ", caps_str); + g_free (caps_str); + g_free (tmpstr); + for (tmp = filenode->streams; tmp; tmp = tmp->next) { + GList *tmp3; + StreamNode *snode = ((StreamNode *) tmp->data); + + STR_APPEND2 (snode->str_open); + + for (tmp2 = snode->frames; tmp2; tmp2 = tmp2->next) { + STR_APPEND3 (((FrameNode *) tmp2->data)->str_open); + } + + tagsnode = snode->tags; + if (tagsnode) { + STR_APPEND3 (tagsnode->str_open); + for (tmp3 = tagsnode->tags; tmp3; tmp3 = tmp3->next) { + STR_APPEND4 (((TagNode *) tmp3->data)->str_open); + } + STR_APPEND3 (tagsnode->str_close); + } + + STR_APPEND2 (snode->str_close); + } + STR_APPEND1 (""); + + tagsnode = filenode->tags; + if (tagsnode) { + STR_APPEND1 (tagsnode->str_open); + for (tmp2 = tagsnode->tags; tmp2; tmp2 = tmp2->next) { + STR_APPEND2 (((TagNode *) tmp2->data)->str_open); + } + STR_APPEND1 (tagsnode->str_close); + } + + g_string_append (res, filenode->str_close); + + return g_string_free (res, FALSE); +} + +/* Public methods */ +GstMediaDescriptorWriter * +gst_media_descriptor_writer_new (GstValidateRunner * runner, + const gchar * uri, GstClockTime duration, gboolean seekable) +{ + GstMediaDescriptorWriter *writer; + FileNode *fnode; + + writer = + g_object_new (GST_TYPE_MEDIA_DESCRIPTOR_WRITER, "validate-runner", runner, + NULL); + + fnode = ((GstMediaDescriptor *) writer)->filenode; + fnode->uri = g_strdup (uri); + fnode->duration = duration; + fnode->seekable = seekable; + fnode->str_open = NULL; + + fnode->str_close = g_markup_printf_escaped (""); + + return writer; +} + +static gboolean +gst_media_descriptor_writer_add_stream (GstMediaDescriptorWriter * writer, + GstDiscovererStreamInfo * info) +{ + const gchar *stype; + gboolean ret = FALSE; + GstCaps *caps; + gchar *capsstr = NULL; + StreamNode *snode = NULL; + + g_return_val_if_fail (GST_IS_MEDIA_DESCRIPTOR_WRITER (writer), FALSE); + g_return_val_if_fail (((GstMediaDescriptor *) writer)->filenode, FALSE); + + snode = g_slice_new0 (StreamNode); + snode->frames = NULL; + snode->cframe = NULL; + + snode->id = g_strdup (gst_discoverer_stream_info_get_stream_id (info)); + if (snode->id == NULL) { + caps = gst_discoverer_stream_info_get_caps (info); + capsstr = gst_caps_to_string (caps); + + g_slice_free (StreamNode, snode); + GST_VALIDATE_REPORT (writer, FILE_NO_STREAM_ID, + "Stream with caps: %s has no stream ID", capsstr); + gst_caps_unref (caps); + g_free (capsstr); + + return FALSE; + } + + caps = gst_discoverer_stream_info_get_caps (info); + snode->caps = caps; /* Pass ownership */ + capsstr = gst_caps_to_string (caps); + if (GST_IS_DISCOVERER_AUDIO_INFO (info)) { + stype = "audio"; + } else if (GST_IS_DISCOVERER_VIDEO_INFO (info)) { + if (gst_discoverer_video_info_is_image (GST_DISCOVERER_VIDEO_INFO (info))) + stype = "image"; + else + stype = "video"; + } else if (GST_IS_DISCOVERER_SUBTITLE_INFO (info)) { + stype = "subtitle"; + } else { + stype = "Unknown"; + } + + snode->str_open = + g_markup_printf_escaped + ("", stype, capsstr, snode->id); + + snode->str_close = g_markup_printf_escaped (""); + + ((GstMediaDescriptor *) writer)->filenode->streams = + g_list_prepend (((GstMediaDescriptor *) writer)->filenode->streams, + snode); + + if (gst_discoverer_stream_info_get_tags (info)) { + gst_media_descriptor_writer_add_tags (writer, snode->id, + gst_discoverer_stream_info_get_tags (info)); + } + + if (writer->priv->raw_caps == NULL) + writer->priv->raw_caps = gst_caps_copy (caps); + else { + writer->priv->raw_caps = gst_caps_merge (writer->priv->raw_caps, + gst_caps_copy (caps)); + } + g_free (capsstr); + + return ret; +} + +static GstPadProbeReturn +_uridecodebin_probe (GstPad * pad, GstPadProbeInfo * info, + GstMediaDescriptorWriter * writer) +{ + gst_media_descriptor_writer_add_frame (writer, pad, info->data); + + return GST_PAD_PROBE_OK; +} + +static gboolean +_find_stream_id (GstPad * pad, GstEvent ** event, + GstMediaDescriptorWriter * writer) +{ + if (GST_EVENT_TYPE (*event) == GST_EVENT_STREAM_START) { + GList *tmp; + StreamNode *snode = NULL; + const gchar *stream_id; + + gst_event_parse_stream_start (*event, &stream_id); + for (tmp = ((GstMediaDescriptor *) writer)->filenode->streams; tmp; + tmp = tmp->next) { + if (g_strcmp0 (((StreamNode *) tmp->data)->id, stream_id) == 0) { + snode = tmp->data; + + break; + } + } + + if (!snode || snode->pad) { + GST_VALIDATE_REPORT (writer, FILE_NO_STREAM_ID, + "Got pad %s:%s where Discoverer found no stream ID", + GST_DEBUG_PAD_NAME (pad)); + + return TRUE; + } + + snode->pad = gst_object_ref (pad); + + return FALSE; + } + + return TRUE; +} + +static inline GstElement * +_get_parser (GstMediaDescriptorWriter * writer, GstPad * pad) +{ + GList *parsers1, *parsers; + GstElement *parser = NULL; + GstElementFactory *parserfact = NULL; + GstCaps *format; + + format = gst_pad_get_current_caps (pad); + + GST_DEBUG ("Getting list of parsers for format %" GST_PTR_FORMAT, format); + parsers1 = + gst_element_factory_list_filter (writer->priv->parsers, format, + GST_PAD_SRC, FALSE); + parsers = + gst_element_factory_list_filter (parsers1, format, GST_PAD_SINK, FALSE); + gst_plugin_feature_list_free (parsers1); + + if (G_UNLIKELY (parsers == NULL)) { + GST_DEBUG ("Couldn't find any compatible parsers"); + goto beach; + } + + /* Just pick the first one */ + parserfact = parsers->data; + if (parserfact) + parser = gst_element_factory_create (parserfact, NULL); + + gst_plugin_feature_list_free (parsers); + +beach: + if (format) + gst_caps_unref (format); + + return parser; +} + +static void +pad_added_cb (GstElement * decodebin, GstPad * pad, + GstMediaDescriptorWriter * writer) +{ + GList *tmp; + StreamNode *snode = NULL; + GstPad *sinkpad, *srcpad; + + /* Try to plug a parser so we have as much info as possible + * about the encoded stream. */ + GstElement *parser = _get_parser (writer, pad); + GstElement *fakesink = gst_element_factory_make ("fakesink", NULL); + + if (parser) { + sinkpad = gst_element_get_static_pad (parser, "sink"); + gst_bin_add (GST_BIN (writer->priv->pipeline), parser); + gst_element_sync_state_with_parent (parser); + gst_pad_link (pad, sinkpad); + + srcpad = gst_element_get_static_pad (parser, "src"); + } else { + srcpad = pad; + } + + sinkpad = gst_element_get_static_pad (fakesink, "sink"); + gst_bin_add (GST_BIN (writer->priv->pipeline), fakesink); + gst_element_sync_state_with_parent (fakesink); + gst_pad_link (srcpad, sinkpad); + gst_pad_sticky_events_foreach (pad, + (GstPadStickyEventsForeachFunction) _find_stream_id, writer); + + for (tmp = ((GstMediaDescriptor *) writer)->filenode->streams; tmp; + tmp = tmp->next) { + snode = tmp->data; + if (snode->pad == pad && srcpad != pad) { + gst_object_unref (pad); + snode->pad = gst_object_ref (srcpad); + break; + } + } + + gst_pad_add_probe (srcpad, GST_PAD_PROBE_TYPE_BUFFER, + (GstPadProbeCallback) _uridecodebin_probe, writer, NULL); +} + +static gboolean +bus_callback (GstBus * bus, GstMessage * message, + GstMediaDescriptorWriter * writer) +{ + GMainLoop *loop = writer->priv->loop; + + switch (GST_MESSAGE_TYPE (message)) { + case GST_MESSAGE_ERROR: + { + GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (writer->priv->pipeline), + GST_DEBUG_GRAPH_SHOW_ALL, "gst-validate-media-check.error"); + g_main_loop_quit (loop); + break; + } + case GST_MESSAGE_EOS: + GST_INFO ("Got EOS!"); + g_main_loop_quit (loop); + break; + case GST_MESSAGE_STATE_CHANGED: + if (GST_MESSAGE_SRC (message) == GST_OBJECT (writer->priv->pipeline)) { + GstState oldstate, newstate, pending; + + gst_message_parse_state_changed (message, &oldstate, &newstate, + &pending); + + GST_DEBUG ("State changed (old: %s, new: %s, pending: %s)", + gst_element_state_get_name (oldstate), + gst_element_state_get_name (newstate), + gst_element_state_get_name (pending)); + + if (newstate == GST_STATE_PLAYING) { + GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (writer->priv->pipeline), + GST_DEBUG_GRAPH_SHOW_ALL, + "gst-validate-media-descriptor-writer.playing"); + } + } + + break; + case GST_MESSAGE_BUFFERING:{ + gint percent; + + gst_message_parse_buffering (message, &percent); + g_print ("%s %d%% \r", "Buffering...", percent); + + /* no state management needed for live pipelines */ + if (percent == 100) { + gst_element_set_state (writer->priv->pipeline, GST_STATE_PLAYING); + } else { + gst_element_set_state (writer->priv->pipeline, GST_STATE_PAUSED); + } + break; + } + default: + break; + } + + return TRUE; +} + +static gboolean +_run_frame_analysis (GstMediaDescriptorWriter * writer, + GstValidateRunner * runner, const gchar * uri) +{ + GstBus *bus; + GstStateChangeReturn sret; + GstValidateMonitor *monitor; + + GstElement *uridecodebin = gst_element_factory_make ("uridecodebin", NULL); + + writer->priv->pipeline = gst_pipeline_new ("frame-analysis"); + + monitor = + gst_validate_monitor_factory_create (GST_OBJECT_CAST (writer-> + priv->pipeline), runner, NULL); + gst_validate_reporter_set_handle_g_logs (GST_VALIDATE_REPORTER (monitor)); + + g_object_set (uridecodebin, "uri", uri, "caps", writer->priv->raw_caps, NULL); + g_signal_connect (uridecodebin, "pad-added", G_CALLBACK (pad_added_cb), + writer); + gst_bin_add (GST_BIN (writer->priv->pipeline), uridecodebin); + + writer->priv->loop = g_main_loop_new (NULL, FALSE); + bus = gst_element_get_bus (writer->priv->pipeline); + gst_bus_add_signal_watch (bus); + g_signal_connect (bus, "message", (GCallback) bus_callback, writer); + sret = gst_element_set_state (writer->priv->pipeline, GST_STATE_PLAYING); + switch (sret) { + case GST_STATE_CHANGE_FAILURE: + /* ignore, we should get an error message posted on the bus */ + g_print ("Pipeline failed to go to PLAYING state\n"); + return FALSE; + default: + break; + } + + g_main_loop_run (writer->priv->loop); + gst_element_set_state (writer->priv->pipeline, GST_STATE_NULL); + gst_object_unref (writer->priv->pipeline); + writer->priv->pipeline = NULL; + g_main_loop_unref (writer->priv->loop); + writer->priv->loop = NULL; + gst_bus_remove_signal_watch (bus); + gst_object_unref (bus); + + return TRUE; +} + +GstMediaDescriptorWriter * +gst_media_descriptor_writer_new_discover (GstValidateRunner * runner, + const gchar * uri, gboolean full, gboolean handle_g_logs, GError ** err) +{ + GList *tmp, *streams = NULL; + GstDiscovererInfo *info = NULL; + GstDiscoverer *discoverer; + GstDiscovererStreamInfo *streaminfo = NULL; + GstMediaDescriptorWriter *writer = NULL; + GstMediaDescriptor *media_descriptor; + const GstTagList *tags; + + discoverer = gst_discoverer_new (GST_SECOND * 60, err); + + if (discoverer == NULL) { + GST_ERROR ("Could not create discoverer"); + + return NULL; + } + + info = gst_discoverer_discover_uri (discoverer, uri, err); + if (info == NULL + || gst_discoverer_info_get_result (info) != GST_DISCOVERER_OK) { + + GST_ERROR ("Could not discover URI: %s (error: %s(", uri, + err && *err ? (*err)->message : "Unkown"); + + goto out; + } + + writer = + gst_media_descriptor_writer_new (runner, + gst_discoverer_info_get_uri (info), + gst_discoverer_info_get_duration (info), + gst_discoverer_info_get_seekable (info)); + + if (handle_g_logs) + gst_validate_reporter_set_handle_g_logs (GST_VALIDATE_REPORTER (writer)); + + tags = gst_discoverer_info_get_tags (info); + if (tags) + gst_media_descriptor_writer_add_taglist (writer, tags); + + streaminfo = gst_discoverer_info_get_stream_info (info); + + if (GST_IS_DISCOVERER_CONTAINER_INFO (streaminfo)) { + ((GstMediaDescriptor *) writer)->filenode->caps = + gst_discoverer_stream_info_get_caps (GST_DISCOVERER_STREAM_INFO + (streaminfo)); + + streams = gst_discoverer_info_get_stream_list (info); + for (tmp = streams; tmp; tmp = tmp->next) { + gst_media_descriptor_writer_add_stream (writer, tmp->data); + } + } else { + gst_media_descriptor_writer_add_stream (writer, streaminfo); + } + + media_descriptor = (GstMediaDescriptor *) writer; + if (streams == NULL && media_descriptor->filenode->caps) + writer->priv->raw_caps = gst_caps_copy (media_descriptor->filenode->caps); + gst_discoverer_stream_info_list_free (streams); + + + if (full == TRUE) + _run_frame_analysis (writer, runner, uri); + +out: + if (info) + gst_discoverer_info_unref (info); + if (streaminfo) + gst_discoverer_stream_info_unref (streaminfo); + g_object_unref (discoverer); + return writer; +} + +gboolean +gst_media_descriptor_writer_add_tags (GstMediaDescriptorWriter + * writer, const gchar * stream_id, const GstTagList * taglist) +{ + TagsNode *tagsnode; + TagNode *tagnode; + GList *tmp, *tmptag; + + gchar *str_str = NULL; + StreamNode *snode = NULL; + + g_return_val_if_fail (GST_IS_MEDIA_DESCRIPTOR_WRITER (writer), FALSE); + g_return_val_if_fail (((GstMediaDescriptor *) writer)->filenode, FALSE); + + for (tmp = ((GstMediaDescriptor *) writer)->filenode->streams; tmp; + tmp = tmp->next) { + if (g_strcmp0 (((StreamNode *) tmp->data)->id, stream_id) == 0) { + snode = tmp->data; + + break; + } + } + + if (snode == NULL) { + GST_WARNING ("Could not find stream with id: %s", stream_id); + + return FALSE; + } + + if (snode->tags == NULL) { + tagsnode = g_slice_new0 (TagsNode); + tagsnode->str_open = g_markup_printf_escaped (""); + tagsnode->str_close = g_markup_printf_escaped (""); + snode->tags = tagsnode; + } else { + tagsnode = snode->tags; + + for (tmptag = tagsnode->tags; tmptag; tmptag = tmptag->next) { + if (tag_node_compare ((TagNode *) tmptag->data, taglist)) { + GST_DEBUG ("Tag already in... not adding again %" GST_PTR_FORMAT, + taglist); + return TRUE; + } + } + } + + tagnode = g_slice_new0 (TagNode); + tagnode->taglist = gst_tag_list_copy (taglist); + str_str = gst_tag_list_to_string (tagnode->taglist); + tagnode->str_open = + g_markup_printf_escaped ("", str_str); + tagsnode->tags = g_list_prepend (tagsnode->tags, tagnode); + + g_free (str_str); + + return FALSE; +} + +gboolean +gst_media_descriptor_writer_add_pad (GstMediaDescriptorWriter * + writer, GstPad * pad) +{ + GList *tmp; + gboolean ret = FALSE; + GstCaps *caps; + gchar *capsstr = NULL, *padname = NULL; + StreamNode *snode = NULL; + + g_return_val_if_fail (GST_IS_MEDIA_DESCRIPTOR_WRITER (writer), FALSE); + g_return_val_if_fail (((GstMediaDescriptor *) writer)->filenode, FALSE); + + caps = gst_pad_get_current_caps (pad); + for (tmp = ((GstMediaDescriptor *) writer)->filenode->streams; tmp; + tmp = tmp->next) { + StreamNode *streamnode = (StreamNode *) tmp->data; + + if (streamnode->pad == pad) { + goto done; + } + } + + snode = g_slice_new0 (StreamNode); + snode->frames = NULL; + snode->cframe = NULL; + + snode->caps = gst_caps_ref (caps); + snode->pad = gst_object_ref (pad); + + capsstr = gst_caps_to_string (caps); + padname = gst_pad_get_name (pad); + snode->str_open = + g_markup_printf_escaped + ("", padname, capsstr, 0); + + snode->str_close = g_markup_printf_escaped (""); + + ((GstMediaDescriptor *) writer)->filenode->streams = + g_list_prepend (((GstMediaDescriptor *) writer)->filenode->streams, + snode); + +done: + if (caps != NULL) + gst_caps_unref (caps); + g_free (capsstr); + g_free (padname); + + return ret; +} + +gboolean +gst_media_descriptor_writer_add_taglist (GstMediaDescriptorWriter * writer, + const GstTagList * taglist) +{ + gchar *str_str = NULL; + TagsNode *tagsnode; + TagNode *tagnode; + GList *tmptag; + + g_return_val_if_fail (GST_IS_MEDIA_DESCRIPTOR_WRITER (writer), FALSE); + g_return_val_if_fail (((GstMediaDescriptor *) writer)->filenode, FALSE); + + if (((GstMediaDescriptor *) writer)->filenode->tags == NULL) { + tagsnode = g_slice_new0 (TagsNode); + tagsnode->str_open = g_markup_printf_escaped (""); + tagsnode->str_close = g_markup_printf_escaped (""); + ((GstMediaDescriptor *) writer)->filenode->tags = tagsnode; + } else { + tagsnode = ((GstMediaDescriptor *) writer)->filenode->tags; + for (tmptag = tagsnode->tags; tmptag; tmptag = tmptag->next) { + if (tag_node_compare ((TagNode *) tmptag->data, taglist)) { + GST_DEBUG ("Tag already in... not adding again %" GST_PTR_FORMAT, + taglist); + return TRUE; + } + } + } + + tagnode = g_slice_new0 (TagNode); + tagnode->taglist = gst_tag_list_copy (taglist); + str_str = gst_tag_list_to_string (tagnode->taglist); + tagnode->str_open = + g_markup_printf_escaped ("", str_str); + tagsnode->tags = g_list_prepend (tagsnode->tags, tagnode); + + g_free (str_str); + + return FALSE; +} + +gboolean +gst_media_descriptor_writer_add_frame (GstMediaDescriptorWriter + * writer, GstPad * pad, GstBuffer * buf) +{ + GList *tmp; + + g_return_val_if_fail (GST_IS_MEDIA_DESCRIPTOR_WRITER (writer), FALSE); + g_return_val_if_fail (((GstMediaDescriptor *) writer)->filenode, FALSE); + + ((GstMediaDescriptor *) writer)->filenode->frame_detection = TRUE; + GST_MEDIA_DESCRIPTOR_LOCK (writer); + for (tmp = ((GstMediaDescriptor *) writer)->filenode->streams; tmp; + tmp = tmp->next) { + StreamNode *streamnode = (StreamNode *) tmp->data; + + if (streamnode->pad == pad) { + GstMapInfo map; + gchar *checksum; + guint id = g_list_length (streamnode->frames); + FrameNode *fnode = g_slice_new0 (FrameNode); + + g_assert (gst_buffer_map (buf, &map, GST_MAP_READ)); + checksum = g_compute_checksum_for_data (G_CHECKSUM_MD5, + (const guchar *) map.data, map.size); + gst_buffer_unmap (buf, &map); + + fnode->id = id; + fnode->offset = GST_BUFFER_OFFSET (buf); + fnode->offset_end = GST_BUFFER_OFFSET_END (buf); + fnode->duration = GST_BUFFER_DURATION (buf); + fnode->pts = GST_BUFFER_PTS (buf); + fnode->dts = GST_BUFFER_DTS (buf); + fnode->is_keyframe = (GST_BUFFER_FLAG_IS_SET (buf, + GST_BUFFER_FLAG_DELTA_UNIT) == FALSE); + + fnode->str_open = + g_markup_printf_escaped (" ", + fnode->duration, id, fnode->is_keyframe ? "true" : "false", + fnode->offset, fnode->offset_end, fnode->pts, fnode->dts, checksum); + + fnode->str_close = NULL; + + streamnode->frames = g_list_append (streamnode->frames, fnode); + GST_MEDIA_DESCRIPTOR_UNLOCK (writer); + + g_free (checksum); + return TRUE; + } + } + GST_MEDIA_DESCRIPTOR_UNLOCK (writer); + + return FALSE; +} + +gboolean +gst_media_descriptor_writer_write (GstMediaDescriptorWriter * + writer, const gchar * filename) +{ + gboolean ret = FALSE; + gchar *serialized; + + g_return_val_if_fail (GST_IS_MEDIA_DESCRIPTOR_WRITER (writer), FALSE); + g_return_val_if_fail (((GstMediaDescriptor *) writer)->filenode, FALSE); + + serialized = serialize_filenode (writer); + + + if (g_file_set_contents (filename, serialized, -1, NULL) == TRUE) + ret = TRUE; + + + g_free (serialized); + + return ret; +} + +gchar * +gst_media_descriptor_writer_serialize (GstMediaDescriptorWriter * writer) +{ + g_return_val_if_fail (GST_IS_MEDIA_DESCRIPTOR_WRITER (writer), FALSE); + g_return_val_if_fail (((GstMediaDescriptor *) writer)->filenode, FALSE); + + return serialize_filenode (writer); +} diff --git a/validate/gst/validate/media-descriptor-writer.h b/validate/gst/validate/media-descriptor-writer.h new file mode 100644 index 0000000..4684a3e --- /dev/null +++ b/validate/gst/validate/media-descriptor-writer.h @@ -0,0 +1,93 @@ +/** + * Insanity QA system + * + * Copyright (c) 2012, Collabora Ltd + * Author: Thibault Saunier + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef GST_MEDIA_DESCRIPTOR_WRITER_h +#define GST_MEDIA_DESCRIPTOR_WRITER_h + +#include +#include +#include +#include +#include "media-descriptor.h" + +G_BEGIN_DECLS + +GType gst_media_descriptor_writer_get_type (void); + +#define GST_TYPE_MEDIA_DESCRIPTOR_WRITER (gst_media_descriptor_writer_get_type ()) +#define GST_MEDIA_DESCRIPTOR_WRITER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_MEDIA_DESCRIPTOR_WRITER, GstMediaDescriptorWriter)) +#define GST_MEDIA_DESCRIPTOR_WRITER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_MEDIA_DESCRIPTOR_WRITER, GstMediaDescriptorWriterClass)) +#define GST_IS_MEDIA_DESCRIPTOR_WRITER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_MEDIA_DESCRIPTOR_WRITER)) +#define GST_IS_MEDIA_DESCRIPTOR_WRITER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_MEDIA_DESCRIPTOR_WRITER)) +#define GST_MEDIA_DESCRIPTOR_WRITER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_MEDIA_DESCRIPTOR_WRITER, GstMediaDescriptorWriterClass)) + +typedef struct _GstMediaDescriptorWriterPrivate GstMediaDescriptorWriterPrivate; + + +typedef struct { + GstMediaDescriptor parent; + + GstMediaDescriptorWriterPrivate *priv; + +} GstMediaDescriptorWriter; + +typedef struct { + + GstMediaDescriptorClass parent; + +} GstMediaDescriptorWriterClass; + +GstMediaDescriptorWriter * gst_media_descriptor_writer_new_discover (GstValidateRunner *runner, + const gchar *uri, + gboolean full, + gboolean handle_g_logs, + GError **err); + +GstMediaDescriptorWriter * gst_media_descriptor_writer_new (GstValidateRunner *runner, + const gchar *location, + GstClockTime duration, + gboolean seekable); + +gchar * gst_media_descriptor_writer_get_xml_path (GstMediaDescriptorWriter *writer); + +gboolean gst_media_descriptor_writer_detects_frames (GstMediaDescriptorWriter *writer); +GstClockTime gst_media_descriptor_writer_get_duration (GstMediaDescriptorWriter *writer); +gboolean gst_media_descriptor_writer_get_seekable (GstMediaDescriptorWriter * writer); + +gboolean gst_media_descriptor_writer_add_pad (GstMediaDescriptorWriter *writer, + GstPad *pad); +gboolean gst_media_descriptor_writer_add_taglist (GstMediaDescriptorWriter *writer, + const GstTagList *taglist); +gboolean gst_media_descriptor_writer_add_frame (GstMediaDescriptorWriter *writer, + GstPad *pad, + GstBuffer *buf); +gboolean gst_media_descriptor_writer_add_tags (GstMediaDescriptorWriter *writer, + const gchar *stream_id, + const GstTagList *taglist); +gboolean gst_media_descriptor_writer_write (GstMediaDescriptorWriter * writer, + const gchar * filename); +gchar * gst_media_descriptor_writer_serialize (GstMediaDescriptorWriter *writer); + + +G_END_DECLS + +#endif /* GST_MEDIA_DESCRIPTOR_WRITER_h */ diff --git a/validate/gst/validate/media-descriptor.c b/validate/gst/validate/media-descriptor.c new file mode 100644 index 0000000..93f4bc2 --- /dev/null +++ b/validate/gst/validate/media-descriptor.c @@ -0,0 +1,461 @@ +/** + * Gstreamer + * + * Copyright (c) 2012, Collabora Ltd. + * Author: Thibault Saunier + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "media-descriptor.h" + +G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstMediaDescriptor, gst_media_descriptor, + G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (GST_TYPE_VALIDATE_REPORTER, NULL)); + +#define GST_MEDIA_DESCRIPTOR_GET_PRIVATE(o)\ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), GST_TYPE_MEDIA_DESCRIPTOR, GstMediaDescriptorPrivate)) + +static inline void +free_tagnode (TagNode * tagnode) +{ + g_free (tagnode->str_open); + g_free (tagnode->str_close); + if (tagnode->taglist) + gst_tag_list_unref (tagnode->taglist); + + g_slice_free (TagNode, tagnode); +} + +static inline void +free_tagsnode (TagsNode * tagsnode) +{ + g_free (tagsnode->str_open); + g_free (tagsnode->str_close); + g_list_free_full (tagsnode->tags, (GDestroyNotify) free_tagnode); + g_slice_free (TagsNode, tagsnode); +} + +static inline void +free_framenode (FrameNode * framenode) +{ + g_free (framenode->str_open); + g_free (framenode->str_close); + + if (framenode->buf) + gst_buffer_unref (framenode->buf); + + g_slice_free (FrameNode, framenode); +} + +static inline void +free_streamnode (StreamNode * streamnode) +{ + if (streamnode->caps) + gst_caps_unref (streamnode->caps); + + g_list_free_full (streamnode->frames, (GDestroyNotify) free_framenode); + + if (streamnode->pad) + gst_object_unref (streamnode->pad); + + if (streamnode->tags) + free_tagsnode (streamnode->tags); + + if (streamnode->padname) + g_free (streamnode->padname); + + if (streamnode->id) + g_free (streamnode->id); + + g_free (streamnode->str_open); + g_free (streamnode->str_close); + g_slice_free (StreamNode, streamnode); +} + +void +free_filenode (FileNode * filenode) +{ + g_list_free_full (filenode->streams, (GDestroyNotify) free_streamnode); + if (filenode->tags) + free_tagsnode (filenode->tags); + + if (filenode->uri) + g_free (filenode->uri); + + if (filenode->caps) + gst_caps_unref (filenode->caps); + + g_free (filenode->str_open); + g_free (filenode->str_close); + + g_slice_free (FileNode, filenode); +} + +gboolean +tag_node_compare (TagNode * tnode, const GstTagList * tlist) +{ + if (gst_structure_is_equal (GST_STRUCTURE (tlist), + GST_STRUCTURE (tnode->taglist)) == FALSE) { + return FALSE; + } + + tnode->found = TRUE; + + return TRUE; +} + +struct _GstMediaDescriptorPrivate +{ + gpointer dummy; +}; + +enum +{ + PROP_0, + PROP_RUNNER, + PROP_LAST +}; + + +static void +gst_media_descriptor_dispose (GstMediaDescriptor * self) +{ + G_OBJECT_CLASS (gst_media_descriptor_parent_class)->dispose (G_OBJECT (self)); +} + +static void +gst_media_descriptor_finalize (GstMediaDescriptor * self) +{ + if (self->filenode) + free_filenode (self->filenode); + + G_OBJECT_CLASS (gst_media_descriptor_parent_class)->finalize (G_OBJECT + (self)); +} + +static void +gst_media_descriptor_init (GstMediaDescriptor * self) +{ + self->filenode = g_slice_new0 (FileNode); +} + +static void +_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + switch (prop_id) { + case PROP_RUNNER: + /* we assume the runner is valid as long as this scenario is, + * no ref taken */ + gst_validate_reporter_set_runner (GST_VALIDATE_REPORTER (object), + g_value_get_object (value)); + break; + default: + break; + } +} + +static void +_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + switch (prop_id) { + case PROP_RUNNER: + /* we assume the runner is valid as long as this scenario is, + * no ref taken */ + g_value_set_object (value, + gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER (object))); + break; + default: + break; + } +} + +static void +gst_media_descriptor_class_init (GstMediaDescriptorClass * self_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (self_class); + + g_type_class_add_private (self_class, sizeof (GstMediaDescriptorPrivate)); + object_class->dispose = + (void (*)(GObject * object)) gst_media_descriptor_dispose; + object_class->finalize = + (void (*)(GObject * object)) gst_media_descriptor_finalize; + + object_class->get_property = _get_property; + object_class->set_property = _set_property; + + g_object_class_install_property (object_class, PROP_RUNNER, + g_param_spec_object ("validate-runner", "VALIDATE Runner", + "The Validate runner to " "report errors to", + GST_TYPE_VALIDATE_RUNNER, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); +} + +static gint +compare_tags (GstMediaDescriptor * ref, StreamNode * rstream, + StreamNode * cstream) +{ + gboolean found; + TagNode *rtag, *ctag; + GList *rtag_list, *ctag_list; + TagsNode *rtags, *ctags; + + rtags = rstream->tags; + ctags = cstream->tags; + if (rtags == NULL && ctags) + return 1; + else if (!rtags && ctags) { + GList *taglist; + GString *all_tags = g_string_new (NULL); + + for (taglist = ctags->tags; taglist; taglist = taglist->next) { + gchar *stags = + gst_tag_list_to_string (((TagNode *) taglist->data)->taglist); + + g_string_append_printf (all_tags, "%s\n", stags); + g_free (stags); + } + + GST_VALIDATE_REPORT (ref, FILE_TAG_DETECTION_INCORRECT, + "Reference descriptor for stream %s has NO tags" + " but tags found: %s", rstream->id, all_tags->str); + + g_string_free (all_tags, TRUE); + + return 0; + } else if (rtags && !ctags) { + GList *taglist; + GString *all_tags = g_string_new (NULL); + + for (taglist = rtags->tags; taglist; taglist = taglist->next) { + gchar *stags = + gst_tag_list_to_string (((TagNode *) taglist->data)->taglist); + + g_string_append_printf (all_tags, "%s\n", stags); + g_free (stags); + } + + GST_VALIDATE_REPORT (ref, FILE_TAG_DETECTION_INCORRECT, + "Reference descriptor for stream %s has tags:\n %s\n" + " but NO tags found on the stream", rstream->id, all_tags->str); + + g_string_free (all_tags, TRUE); + return 0; + } + + for (rtag_list = rtags->tags; rtag_list; rtag_list = rtag_list->next) { + rtag = rtag_list->data; + found = FALSE; + for (ctag_list = ctags->tags; ctag_list; ctag_list = ctag_list->next) { + ctag = ctag_list->data; + if (gst_tag_list_is_equal (rtag->taglist, ctag->taglist)) { + found = TRUE; + + break; + } + } + + if (found == FALSE) { + gchar *rtaglist = gst_tag_list_to_string (rtag->taglist); + + GST_VALIDATE_REPORT (ref, FILE_TAG_DETECTION_INCORRECT, + "Reference descriptor for stream %s has tags %s" + " but no equivalent taglist was found on the compared stream", + rstream->id, rtaglist); + g_free (rtaglist); + + return 0; + } + } + + return 1; +} + +/* Return -1 if not found 1 if OK 0 if an error occured */ +static gint +comparse_stream (GstMediaDescriptor * ref, StreamNode * rstream, + StreamNode * cstream) +{ + if (g_strcmp0 (rstream->id, cstream->id) == 0) { + if (!gst_caps_is_equal (rstream->caps, cstream->caps)) { + gchar *rcaps = gst_caps_to_string (rstream->caps), + *ccaps = gst_caps_to_string (cstream->caps); + GST_VALIDATE_REPORT (ref, FILE_PROFILE_INCORRECT, + "Reference descriptor for stream %s has caps: %s" + " but compared stream %s has caps: %s", + rstream->id, rcaps, cstream->id, ccaps); + g_free (rcaps); + g_free (ccaps); + return 0; + } + + compare_tags (ref, rstream, cstream); + + return 1; + } + + return -1; +} + +gboolean +gst_media_descriptors_compare (GstMediaDescriptor * ref, + GstMediaDescriptor * compared) +{ + GList *rstream_list; + FileNode *rfilenode = ref->filenode, *cfilenode = compared->filenode; + + if (rfilenode->duration != cfilenode->duration) { + GST_VALIDATE_REPORT (ref, FILE_DURATION_INCORRECT, + "Duration %" GST_TIME_FORMAT " is different from the reference %" + GST_TIME_FORMAT, GST_TIME_ARGS (cfilenode->duration), + GST_TIME_ARGS (rfilenode->duration)); + } + + if (rfilenode->seekable != cfilenode->seekable) { + GST_VALIDATE_REPORT (ref, FILE_SEEKABLE_INCORRECT, + "File known as %s but is reported %s now", + rfilenode->seekable ? "seekable" : "not seekable", + cfilenode->seekable ? "seekable" : "not seekable"); + } + + if (g_list_length (rfilenode->streams) != g_list_length (cfilenode->streams)) { + GST_VALIDATE_REPORT (ref, FILE_PROFILE_INCORRECT, + "Reference descriptor has %i streams != compared which has %i streams", + g_list_length (rfilenode->streams), g_list_length (cfilenode->streams)); + + return FALSE; + } + + + for (rstream_list = rfilenode->streams; rstream_list; + rstream_list = rstream_list->next) { + GList *cstream_list; + gint sfound = -1; + + for (cstream_list = cfilenode->streams; cstream_list; + cstream_list = cstream_list->next) { + + sfound = comparse_stream (ref, rstream_list->data, cstream_list->data); + if (sfound == 0) { + return FALSE; + } else if (sfound == 1) { + break; + } + } + + if (sfound == FALSE) { + GST_VALIDATE_REPORT (ref, FILE_PROFILE_INCORRECT, + "Could not find stream %s in the compared descriptor", + ((StreamNode *) rstream_list->data)->id); + + return FALSE; + } + } + + return TRUE; +} + +gboolean +gst_media_descriptor_detects_frames (GstMediaDescriptor * self) +{ + g_return_val_if_fail (GST_IS_MEDIA_DESCRIPTOR (self), FALSE); + g_return_val_if_fail (self->filenode, FALSE); + + return self->filenode->frame_detection; +} + +gboolean +gst_media_descriptor_get_buffers (GstMediaDescriptor * self, + GstPad * pad, GCompareFunc compare_func, GList ** bufs) +{ + GList *tmpstream, *tmpframe; + gboolean check = (pad == NULL), ret = FALSE; + GstCaps *pad_caps = gst_pad_get_current_caps (pad); + + g_return_val_if_fail (GST_IS_MEDIA_DESCRIPTOR (self), FALSE); + g_return_val_if_fail (self->filenode, FALSE); + + for (tmpstream = self->filenode->streams; + tmpstream; tmpstream = tmpstream->next) { + StreamNode *streamnode = (StreamNode *) tmpstream->data; + + if (pad && streamnode->pad == pad) + check = TRUE; + + if (!streamnode->pad && gst_caps_is_subset (pad_caps, streamnode->caps)) { + check = TRUE; + } + + if (check) { + ret = TRUE; + for (tmpframe = streamnode->frames; tmpframe; tmpframe = tmpframe->next) { + if (compare_func) + *bufs = + g_list_insert_sorted (*bufs, + gst_buffer_ref (((FrameNode *) tmpframe->data)->buf), + compare_func); + else + *bufs = + g_list_prepend (*bufs, + gst_buffer_ref (((FrameNode *) tmpframe->data)->buf)); + } + + if (pad != NULL) + goto done; + } + } + + +done: + + if (compare_func == NULL) + *bufs = g_list_reverse (*bufs); + + gst_caps_unref (pad_caps); + return ret; +} + +GstClockTime +gst_media_descriptor_get_duration (GstMediaDescriptor * self) +{ + g_return_val_if_fail (GST_IS_MEDIA_DESCRIPTOR (self), FALSE); + g_return_val_if_fail (self->filenode, FALSE); + + return self->filenode->duration; +} + +gboolean +gst_media_descriptor_get_seekable (GstMediaDescriptor * self) +{ + g_return_val_if_fail (GST_IS_MEDIA_DESCRIPTOR (self), FALSE); + g_return_val_if_fail (self->filenode, FALSE); + + return self->filenode->seekable; +} + +GList * +gst_media_descriptor_get_pads (GstMediaDescriptor * self) +{ + GList *ret = NULL, *tmp; + + for (tmp = self->filenode->streams; tmp; tmp = tmp->next) { + StreamNode *snode = (StreamNode *) tmp->data; + ret = g_list_append (ret, gst_pad_new (snode->padname, GST_PAD_UNKNOWN)); + } + + return ret; +} diff --git a/validate/gst/validate/media-descriptor.h b/validate/gst/validate/media-descriptor.h new file mode 100644 index 0000000..48a58f2 --- /dev/null +++ b/validate/gst/validate/media-descriptor.h @@ -0,0 +1,159 @@ +/** + * GstValidate + * + * Copyright (c) 2012, Collabora Ltd. + * Author: Thibault Saunier + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_MEDIA_DESCRIPTOR_H__ +#define __GST_MEDIA_DESCRIPTOR_H__ + +#include +#include +#include +#include "gst-validate-report.h" + +G_BEGIN_DECLS + +typedef struct +{ + /* Children */ + /* TagNode */ + GList *tags; + + gchar *str_open; + gchar *str_close; +} TagsNode; + +/* Parsing structures */ +typedef struct +{ + /* Children */ + /* StreamNode */ + GList *streams; + /* TagsNode */ + TagsNode *tags; + + /* attributes */ + guint64 id; + gchar *uri; + GstClockTime duration; + gboolean frame_detection; + gboolean seekable; + + GstCaps *caps; + + gchar *str_open; + gchar *str_close; +} FileNode; + +typedef struct +{ + /* Children */ + GstTagList *taglist; + + /* Testing infos */ + gboolean found; + + gchar *str_open; + gchar *str_close; +} TagNode; + +typedef struct +{ + /* Children */ + /* FrameNode */ + GList *frames; + + /* TagsNode */ + TagsNode *tags; + + /* Attributes */ + GstCaps *caps; + gchar *id; + gchar *padname; + + /* Testing infos */ + GstPad *pad; + GList *cframe; + + gchar *str_open; + gchar *str_close; +} StreamNode; + +typedef struct +{ + /* Attributes */ + guint64 id; + guint64 offset; + guint64 offset_end; + GstClockTime duration; + GstClockTime pts, dts; + gboolean is_keyframe; + + GstBuffer *buf; + + gchar *checksum; + gchar *str_open; + gchar *str_close; +} FrameNode; + +void free_filenode (FileNode * filenode); +gboolean tag_node_compare (TagNode * tnode, const GstTagList * tlist); + +GType gst_media_descriptor_get_type (void); + +#define GST_TYPE_MEDIA_DESCRIPTOR (gst_media_descriptor_get_type ()) +#define GST_MEDIA_DESCRIPTOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_MEDIA_DESCRIPTOR, GstMediaDescriptor)) +#define GST_MEDIA_DESCRIPTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_MEDIA_DESCRIPTOR, GstMediaDescriptorClass)) +#define GST_IS_MEDIA_DESCRIPTOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_MEDIA_DESCRIPTOR)) +#define GST_IS_MEDIA_DESCRIPTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_MEDIA_DESCRIPTOR)) +#define GST_MEDIA_DESCRIPTOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_MEDIA_DESCRIPTOR, GstMediaDescriptorClass)) + +#define GST_MEDIA_DESCRIPTOR_GET_LOCK(obj) (&GST_MEDIA_DESCRIPTOR(obj)->lock) +#define GST_MEDIA_DESCRIPTOR_LOCK(obj) g_mutex_lock(GST_MEDIA_DESCRIPTOR_GET_LOCK(obj)) +#define GST_MEDIA_DESCRIPTOR_UNLOCK(obj) g_mutex_unlock(GST_MEDIA_DESCRIPTOR_GET_LOCK(obj)) + +typedef struct _GstMediaDescriptorPrivate GstMediaDescriptorPrivate; + +typedef struct { + GObject parent; + + FileNode *filenode; + + GMutex lock; + + GstMediaDescriptorPrivate *priv; +} GstMediaDescriptor; + +typedef struct { + GObjectClass parent; + +} GstMediaDescriptorClass; + +gboolean gst_media_descriptors_compare (GstMediaDescriptor *ref, + GstMediaDescriptor *compared); +gboolean gst_media_descriptor_detects_frames (GstMediaDescriptor * self); +gboolean gst_media_descriptor_get_buffers (GstMediaDescriptor * self, + GstPad * pad, GCompareFunc compare_func, GList ** bufs); +GstClockTime gst_media_descriptor_get_duration (GstMediaDescriptor * self); +gboolean gst_media_descriptor_get_seekable (GstMediaDescriptor * self); +GList * gst_media_descriptor_get_pads (GstMediaDescriptor * self); +G_END_DECLS + +#endif diff --git a/validate/gst/validate/validate.c b/validate/gst/validate/validate.c new file mode 100644 index 0000000..08dd49e --- /dev/null +++ b/validate/gst/validate/validate.c @@ -0,0 +1,280 @@ +/* GStreamer + * + * Copyright (C) 2013 Collabora Ltd. + * Author: Thiago Sousa Santos + * + * validate.c - Validate generic functions + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/** + * SECTION:validate + * @short_description: Initialize GstValidate + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +/* For g_stat () */ +#include +#include +#include +#include + +#include "validate.h" +#include "gst-validate-utils.h" +#include "gst-validate-internal.h" + +#ifdef G_OS_WIN32 +#define WIN32_LEAN_AND_MEAN /* prevents from including too many things */ +#include /* GetStdHandle, windows console */ + +HMODULE _priv_gstvalidate_dll_handle = NULL; +#endif + +GST_DEBUG_CATEGORY (gstvalidate_debug); + +static GMutex _gst_validate_registry_mutex; +static GstRegistry *_gst_validate_registry_default = NULL; + +static GList *core_config = NULL; +static gboolean validate_initialized = FALSE; + +#ifdef G_OS_WIN32 +BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved); +BOOL WINAPI +DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + if (fdwReason == DLL_PROCESS_ATTACH) + _priv_gstvalidate_dll_handle = (HMODULE) hinstDLL; + + return TRUE; +} + +#endif + +static GstRegistry * +gst_validate_registry_get (void) +{ + GstRegistry *registry; + + g_mutex_lock (&_gst_validate_registry_mutex); + if (G_UNLIKELY (!_gst_validate_registry_default)) { + _gst_validate_registry_default = g_object_newv (GST_TYPE_REGISTRY, 0, NULL); + gst_object_ref_sink (GST_OBJECT_CAST (_gst_validate_registry_default)); + } + registry = _gst_validate_registry_default; + g_mutex_unlock (&_gst_validate_registry_mutex); + + return registry; +} + +#define GST_VALIDATE_PLUGIN_CONFIG "gst-validate-plugin-config" + +static void +_free_plugin_config (gpointer data) +{ + g_list_free_full (data, (GDestroyNotify) gst_structure_free); +} + +static GList * +create_config (const gchar * path, const gchar * suffix) +{ + GList *structures = NULL, *tmp, *result = NULL; + + if (!suffix) + return NULL; + + structures = gst_validate_utils_structs_parse_from_filename (path); + + for (tmp = structures; tmp; tmp = tmp->next) { + GstStructure *structure = tmp->data; + + if (gst_structure_has_name (structure, suffix)) + result = g_list_append (result, structure); + else + gst_structure_free (structure); + } + + g_list_free (structures); + return result; +} + +/** + * gst_validate_plugin_get_config: + * @plugin, a #GstPlugin, or #NULL + * + * Return the configuration specific to @plugin, or the "core" one if @plugin + * is #NULL + * + * Returns: (transfer none) (element-type GstStructure): a list of #GstStructure + */ +GList * +gst_validate_plugin_get_config (GstPlugin * plugin) +{ + GList *plugin_conf = NULL; + const gchar *suffix; + const gchar *config; + GStrv tmp; + guint i; + + if (plugin) { + if ((plugin_conf = + g_object_get_data (G_OBJECT (plugin), GST_VALIDATE_PLUGIN_CONFIG))) + return plugin_conf; + + suffix = gst_plugin_get_name (plugin); + } else { + if (core_config) + return core_config; + + suffix = "core"; + } + + config = g_getenv ("GST_VALIDATE_CONFIG"); + if (!config) + return NULL; + + tmp = g_strsplit (config, G_SEARCHPATH_SEPARATOR_S, -1); + for (i = 0; tmp[i] != NULL; i++) { + GList *l; + + l = create_config (tmp[i], suffix); + if (l) + plugin_conf = g_list_concat (plugin_conf, l); + } + g_strfreev (tmp); + + if (plugin) + g_object_set_data_full (G_OBJECT (plugin), GST_VALIDATE_PLUGIN_CONFIG, + plugin_conf, _free_plugin_config); + else + core_config = plugin_conf; + + return plugin_conf; +} + +static void +gst_validate_init_plugins (void) +{ + GstRegistry *registry; + const gchar *plugin_path; + + gst_registry_fork_set_enabled (FALSE); + registry = gst_validate_registry_get (); + + plugin_path = g_getenv ("GST_VALIDATE_PLUGIN_PATH"); + if (plugin_path) { + char **list; + int i; + + GST_DEBUG ("GST_VALIDATE_PLUGIN_PATH set to %s", plugin_path); + list = g_strsplit (plugin_path, G_SEARCHPATH_SEPARATOR_S, 0); + for (i = 0; list[i]; i++) { + gst_registry_scan_path (registry, list[i]); + } + g_strfreev (list); + } else { + GST_DEBUG ("GST_VALIDATE_PLUGIN_PATH not set"); + } + + if (plugin_path == NULL) { + char *home_plugins; + + /* plugins in the user's home directory take precedence over + * system-installed ones */ + home_plugins = g_build_filename (g_get_user_data_dir (), + "gstreamer-" GST_API_VERSION, "plugins", NULL); + + GST_DEBUG ("scanning home plugins %s", home_plugins); + gst_registry_scan_path (registry, home_plugins); + g_free (home_plugins); + + /* add the main (installed) library path */ + +#ifdef G_OS_WIN32 + { + char *base_dir; + char *dir; + + base_dir = + g_win32_get_package_installation_directory_of_module + (_priv_gstvalidate_dll_handle); + + dir = g_build_filename (base_dir, + "lib", "gstreamer-" GST_API_VERSION, "validate", NULL); + + GST_DEBUG ("scanning DLL dir %s", dir); + + gst_registry_scan_path (registry, dir); + + g_free (dir); + g_free (base_dir); + } +#else + gst_registry_scan_path (registry, PLUGINDIR); +#endif + } + gst_registry_fork_set_enabled (TRUE); +} + +/** + * gst_validate_init: + * + * Initializes GstValidate, call that before any usage of GstValidate. + * You should take care of initilizing GStreamer before calling this + * function. + */ +void +gst_validate_init (void) +{ + if (validate_initialized) { + return; + } + + GST_DEBUG_CATEGORY_INIT (gstvalidate_debug, "validate", 0, + "Validation library"); + + /* init the report system (can be called multiple times) */ + gst_validate_report_init (); + + /* Init the scenario system */ + init_scenarios (); + + /* Ensure we load overrides before any use of a monitor */ + gst_validate_override_registry_preload (); + + validate_initialized = TRUE; + + gst_validate_init_plugins (); +} + +void +gst_validate_deinit (void) +{ + _free_plugin_config (core_config); + gst_object_unref (_gst_validate_registry_default); + _priv_validate_override_registry_deinit (); + core_config = NULL; + validate_initialized = FALSE; +} + +gboolean +gst_validate_is_initialized (void) +{ + return validate_initialized; +} diff --git a/validate/gst/validate/validate.h b/validate/gst/validate/validate.h new file mode 100644 index 0000000..254571d --- /dev/null +++ b/validate/gst/validate/validate.h @@ -0,0 +1,23 @@ +/* GStreamer + * Copyright (C) 2013 Thiago Santos + */ + +#ifndef _GST_VALIDATE_H +#define _GST_VALIDATE_H + +#include +#include + +#include +#include +#include +#include +#include +#include + +void gst_validate_init (void); +void gst_validate_deinit (void); +GList * gst_validate_plugin_get_config (GstPlugin * plugin); +gboolean gst_validate_is_initialized (void); + +#endif /* _GST_VALIDATE_H */ diff --git a/validate/launcher/Makefile.am b/validate/launcher/Makefile.am new file mode 100644 index 0000000..b4fd617 --- /dev/null +++ b/validate/launcher/Makefile.am @@ -0,0 +1,20 @@ +launcherdir = $(libdir)/gst-validate-launcher/python/launcher/ + +SUBDIRS = \ + apps + +launcher_PYTHON = \ + baseclasses.py \ + __init__.py \ + loggable.py \ + reporters.py \ + main.py \ + httpserver.py \ + RangeHTTPServer.py \ + utils.py \ + vfb_server.py \ + config.py + +clean-local: + rm -rf *.pyc *.pyo + diff --git a/validate/launcher/RangeHTTPServer.py b/validate/launcher/RangeHTTPServer.py new file mode 100644 index 0000000..9f97c47 --- /dev/null +++ b/validate/launcher/RangeHTTPServer.py @@ -0,0 +1,294 @@ +#!/usr/bin/env python2 + +# Portions Copyright (C) 2009,2010 Xyne +# Portions Copyright (C) 2011 Sean Goller +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# (version 2) as published by the Free Software Foundation. +# +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + + +"""Range HTTP Server. + +This module builds on BaseHTTPServer by implementing the standard GET +and HEAD requests in a fairly straightforward manner, and includes support +for the Range header. + +""" + + +__version__ = "0.1" + +__all__ = ["RangeHTTPRequestHandler"] + +import os +import sys +import posixpath +import BaseHTTPServer +from SocketServer import ThreadingMixIn +import urllib +import cgi +import shutil +import mimetypes +import time +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO + + +_bandwidth = 0 + + +class RangeHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): + + """Simple HTTP request handler with GET and HEAD commands. + + This serves files from the current directory and any of its + subdirectories. The MIME type for files is determined by + calling the .guess_type() method. + + The GET and HEAD requests are identical except that the HEAD + request omits the actual contents of the file. + + """ + + server_version = "RangeHTTP/" + __version__ + + def do_GET(self): + """Serve a GET request.""" + f, start_range, end_range = self.send_head() + print "Got values of ", start_range, " and ", end_range, "...\n" + if f: + f.seek(start_range, 0) + chunk = 0x1000 + total = 0 + while chunk > 0: + if start_range + chunk > end_range: + chunk = end_range - start_range + + if _bandwidth != 0: + time_to_sleep = float(float(chunk) / float(_bandwidth)) + time.sleep(time_to_sleep) + + try: + self.wfile.write(f.read(chunk)) + except: + break + total += chunk + start_range += chunk + f.close() + + def do_HEAD(self): + """Serve a HEAD request.""" + f, start_range, end_range = self.send_head() + if f: + f.close() + + def send_head(self): + """Common code for GET and HEAD commands. + + This sends the response code and MIME headers. + + Return value is either a file object (which has to be copied + to the outputfile by the caller unless the command was HEAD, + and must be closed by the caller under all circumstances), or + None, in which case the caller has nothing further to do. + + """ + path = self.translate_path(self.path) + f = None + if os.path.isdir(path): + if not self.path.endswith('/'): + # redirect browser - doing basically what apache does + self.send_response(301) + self.send_header("Location", self.path + "/") + self.end_headers() + return (None, 0, 0) + for index in "index.html", "index.htm": + index = os.path.join(path, index) + if os.path.exists(index): + path = index + break + else: + return self.list_directory(path) + ctype = self.guess_type(path) + try: + # Always read in binary mode. Opening files in text mode may cause + # newline translations, making the actual size of the content + # transmitted *less* than the content-length! + f = open(path, 'rb') + except IOError: + self.send_error(404, "File not found") + return (None, 0, 0) + if "Range" in self.headers: + self.send_response(206) + else: + self.send_response(200) + self.send_header("Content-type", ctype) + fs = os.fstat(f.fileno()) + size = int(fs[6]) + start_range = 0 + end_range = size + self.send_header("Accept-Ranges", "bytes") + if "Range" in self.headers: + s, e = self.headers['range'][6:].split('-', 1) + sl = len(s) + el = len(e) + if sl > 0: + start_range = int(s) + if el > 0: + end_range = int(e) + 1 + elif el > 0: + ei = int(e) + if ei < size: + start_range = size - ei + self.send_header("Content-Range", 'bytes ' + str( + start_range) + '-' + str(end_range - 1) + '/' + str(size)) + self.send_header("Content-Length", end_range - start_range) + self.send_header("Last-Modified", self.date_time_string(fs.st_mtime)) + self.end_headers() + return (f, start_range, end_range) + + def list_directory(self, path): + """Helper to produce a directory listing (absent index.html). + + Return value is either a file object, or None (indicating an + error). In either case, the headers are sent, making the + interface the same as for send_head(). + + """ + try: + list = os.listdir(path) + except os.error: + self.send_error(404, "No permission to list directory") + return None + list.sort(key=lambda a: a.lower()) + f = StringIO() + displaypath = cgi.escape(urllib.unquote(self.path)) + f.write('') + f.write("\nDirectory listing for %s\n" % + displaypath) + f.write("\n

Directory listing for %s

\n" % displaypath) + f.write("
\n
    \n") + for name in list: + fullname = os.path.join(path, name) + displayname = linkname = name + # Append / for directories or @ for symbolic links + if os.path.isdir(fullname): + displayname = name + "/" + linkname = name + "/" + if os.path.islink(fullname): + displayname = name + "@" + # Note: a link to a directory displays with @ and links with / + f.write('
  • %s\n' + % (urllib.quote(linkname), cgi.escape(displayname))) + f.write("
\n
\n\n\n") + length = f.tell() + f.seek(0) + self.send_response(200) + self.send_header("Content-type", "text/html") + self.send_header("Content-Length", str(length)) + self.end_headers() + return (f, 0, length) + + def translate_path(self, path): + """Translate a /-separated PATH to the local filename syntax. + + Components that mean special things to the local file system + (e.g. drive or directory names) are ignored. (XXX They should + probably be diagnosed.) + + """ + # abandon query parameters + path = path.split('?', 1)[0] + path = path.split('#', 1)[0] + path = posixpath.normpath(urllib.unquote(path)) + words = path.split('/') + words = filter(None, words) + path = os.getcwd() + for word in words: + drive, word = os.path.splitdrive(word) + head, word = os.path.split(word) + if word in (os.curdir, os.pardir): + continue + path = os.path.join(path, word) + return path + + def copyfile(self, source, outputfile): + """Copy all data between two file objects. + + The SOURCE argument is a file object open for reading + (or anything with a read() method) and the DESTINATION + argument is a file object open for writing (or + anything with a write() method). + + The only reason for overriding this would be to change + the block size or perhaps to replace newlines by CRLF + -- note however that this the default server uses this + to copy binary data as well. + + """ + shutil.copyfileobj(source, outputfile) + + def guess_type(self, path): + """Guess the type of a file. + + Argument is a PATH (a filename). + + Return value is a string of the form type/subtype, + usable for a MIME Content-type header. + + The default implementation looks the file's extension + up in the table self.extensions_map, using application/octet-stream + as a default; however it would be permissible (if + slow) to look inside the data to make a better guess. + + """ + + base, ext = posixpath.splitext(path) + if ext in self.extensions_map: + return self.extensions_map[ext] + ext = ext.lower() + if ext in self.extensions_map: + return self.extensions_map[ext] + else: + return self.extensions_map[''] + + if not mimetypes.inited: + mimetypes.init() # try to read system mime.types + extensions_map = mimetypes.types_map.copy() + extensions_map.update({ + '': 'application/octet-stream', # Default + '.py': 'text/plain', + '.c': 'text/plain', + '.h': 'text/plain', + '.mp4': 'video/mp4', + '.ogg': 'video/ogg', + }) + + +class ThreadedHTTPServer(ThreadingMixIn, BaseHTTPServer.HTTPServer): + """Handle requests in a separate thread.""" + + +def test(HandlerClass=RangeHTTPRequestHandler, + ServerClass=ThreadedHTTPServer): + BaseHTTPServer.test(HandlerClass, ServerClass) + + +if __name__ == '__main__': + if len(sys.argv) > 2: + _bandwidth = int(sys.argv[2]) + test() diff --git a/validate/launcher/__init__.py b/validate/launcher/__init__.py new file mode 100644 index 0000000..4d1ecfc --- /dev/null +++ b/validate/launcher/__init__.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python2 +# +# Copyright (c) 2014,Thibault Saunier +# +# This program 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.1 of the License, or (at your option) any later version. +# +# This program 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 program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301, USA. diff --git a/validate/launcher/apps/Makefile.am b/validate/launcher/apps/Makefile.am new file mode 100644 index 0000000..33811cb --- /dev/null +++ b/validate/launcher/apps/Makefile.am @@ -0,0 +1,7 @@ +appsdir = $(libdir)/gst-validate-launcher/python/launcher/apps/ + +SUBDIRS = + +apps_PYTHON = \ + __init__.py \ + gstvalidate.py diff --git a/validate/launcher/apps/__init__.py b/validate/launcher/apps/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/validate/launcher/apps/gstvalidate.py b/validate/launcher/apps/gstvalidate.py new file mode 100644 index 0000000..af35e49 --- /dev/null +++ b/validate/launcher/apps/gstvalidate.py @@ -0,0 +1,839 @@ +#!/usr/bin/env python2 +# +# Copyright (c) 2013,Thibault Saunier +# +# This program 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.1 of the License, or (at your option) any later version. +# +# This program 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 program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301, USA. +import os +import sys +import time +import urlparse +import subprocess +import ConfigParser +from launcher.loggable import Loggable + +from launcher.baseclasses import GstValidateTest, Test, \ + ScenarioManager, NamedDic, GstValidateTestsGenerator, \ + GstValidateMediaDescriptor, GstValidateEncodingTestInterface, \ + GstValidateBaseTestManager, MediaDescriptor, MediaFormatCombination + +from launcher.utils import path2url, DEFAULT_TIMEOUT, which, \ + GST_SECOND, Result, Protocols, mkdir, printc, Colors, get_data_file + +# +# Private global variables # +# + +# definitions of commands to use +GST_VALIDATE_COMMAND = "gst-validate-1.0" +GST_VALIDATE_TRANSCODING_COMMAND = "gst-validate-transcoding-1.0" +G_V_DISCOVERER_COMMAND = "gst-validate-media-check-1.0" +if "win32" in sys.platform: + GST_VALIDATE_COMMAND += ".exe" + GST_VALIDATE_TRANSCODING_COMMAND += ".exe" + G_V_DISCOVERER_COMMAND += ".exe" + +AUDIO_ONLY_FILE_TRANSCODING_RATIO = 5 + +# +# API to be used to create testsuites # +# + +""" +Some info about protocols and how to handle them +""" +GST_VALIDATE_CAPS_TO_PROTOCOL = [("application/x-hls", Protocols.HLS), + ("application/dash+xml", Protocols.DASH)] +GST_VALIDATE_PROTOCOL_TIMEOUTS = {Protocols.HTTP: 120, + Protocols.HLS: 240, + Protocols.DASH: 240} + + +class GstValidateMediaCheckTestsGenerator(GstValidateTestsGenerator): + + def __init__(self, test_manager): + GstValidateTestsGenerator.__init__(self, "media_check", test_manager) + + def populate_tests(self, uri_minfo_special_scenarios, scenarios): + for uri, mediainfo, special_scenarios in uri_minfo_special_scenarios: + protocol = mediainfo.media_descriptor.get_protocol() + try: + timeout = GST_VALIDATE_PROTOCOL_TIMEOUTS[protocol] + except KeyError: + timeout = DEFAULT_TIMEOUT + + classname = "validate.%s.media_check.%s" % (protocol, + os.path.basename(uri).replace(".", "_")) + self.add_test(GstValidateMediaCheckTest(classname, + self.test_manager.options, + self.test_manager.reporter, + mediainfo.media_descriptor, + uri, + mediainfo.path, + timeout=timeout)) + + +class GstValidateTranscodingTestsGenerator(GstValidateTestsGenerator): + + def __init__(self, test_manager): + GstValidateTestsGenerator.__init__(self, "transcode", test_manager) + + def populate_tests(self, uri_minfo_special_scenarios, scenarios): + for uri, mediainfo, special_scenarios in uri_minfo_special_scenarios: + if mediainfo.media_descriptor.is_image(): + continue + + for comb in self.test_manager.get_encoding_formats(): + classname = "validate.%s.transcode.to_%s.%s" % (mediainfo.media_descriptor.get_protocol(), + str(comb).replace( + ' ', '_'), + mediainfo.media_descriptor.get_clean_name()) + self.add_test(GstValidateTranscodingTest(classname, + self.test_manager.options, + self.test_manager.reporter, + comb, + uri, + mediainfo.media_descriptor)) + + +class FakeMediaDescriptor(MediaDescriptor): + def __init__(self, infos, pipeline_desc): + MediaDescriptor.__init__(self) + self._infos = infos + self._pipeline_desc = pipeline_desc + + def get_path(self): + return self._infos.get('path', None) + + def get_media_filepath(self): + return self._infos.get('media-filepath', None) + + def get_caps(self): + return self._infos.get('caps', None) + + def get_uri(self): + return self._infos.get('uri', None) + + def get_duration(self): + return int(self._infos.get('duration', 0)) * GST_SECOND + + def get_protocol(self): + return self._infos.get('protocol', "launch_pipeline") + + def is_seekable(self): + return self._infos.get('is-seekable', True) + + def is_image(self): + return self._infos.get('is-image', False) + + def get_num_tracks(self, track_type): + return self._infos.get('num-%s-tracks' % track_type, + self._pipeline_desc.count(track_type + "sink")) + + def can_play_reverse(self): + return self._infos.get('plays-reverse', False) + + +class GstValidatePipelineTestsGenerator(GstValidateTestsGenerator): + + def __init__(self, name, test_manager, pipeline_template=None, + pipelines_descriptions=None, valid_scenarios=[]): + """ + @name: The name of the generator + @pipeline_template: A template pipeline to be used to generate actual pipelines + @pipelines_descriptions: A list of tuple of the form: + (test_name, pipeline_description, extra_data) + extra_data being a dictionnary with the follwing keys: + 'scenarios': ["the", "valide", "scenarios", "names"] + 'duration': the_duration # in seconds + 'timeout': a_timeout # in seconds + 'hard_timeout': a_hard_timeout # in seconds + + @valid_scenarios: A list of scenario name that can be used with that generator + """ + GstValidateTestsGenerator.__init__(self, name, test_manager) + self._pipeline_template = pipeline_template + self._pipelines_descriptions = pipelines_descriptions + self._valid_scenarios = valid_scenarios + + def get_fname(self, scenario, protocol=None, name=None): + if name is None: + name = self.name + + if protocol is not None: + protocol_str = "%s." % protocol + else: + protocol_str = "" + + if scenario is not None and scenario.name.lower() != "none": + return "%s.%s%s.%s" % ("validate", protocol_str, name, scenario.name) + + return ("%s.%s.%s.%s" % ("validate", protocol_str, self.name, name)).replace("..", ".") + + def generate_tests(self, uri_minfo_special_scenarios, scenarios): + if self._valid_scenarios is None: + scenarios = [None] + elif self._valid_scenarios: + scenarios = [scenario for scenario in scenarios if + scenario is not None and scenario.name in self._valid_scenarios] + + return super(GstValidatePipelineTestsGenerator, self).generate_tests( + uri_minfo_special_scenarios, scenarios) + + def populate_tests(self, uri_minfo_special_scenarios, scenarios): + for description in self._pipelines_descriptions: + name = description[0] + pipeline = description[1] + if len(description) == 3: + extra_datas = description[2] + else: + extra_datas = {} + + for scenario in extra_datas.get('scenarios', scenarios): + if isinstance(scenario, str): + scenario = self.test_manager.scenarios_manager.get_scenario(scenario) + + mediainfo = FakeMediaDescriptor(extra_datas, pipeline) + if not mediainfo.is_compatible(scenario): + continue + + if self.test_manager.options.mute: + if scenario and scenario.needs_clock_sync(): + fakesink = "fakesink sync=true" + else: + fakesink = "fakesink" + + audiosink = videosink = fakesink + else: + audiosink = 'autoaudiosink' + videosink = 'autovideosink' + + pipeline_desc = pipeline % {'videosink': videosink, + 'audiosink': audiosink} + + fname = self.get_fname(scenario, protocol=mediainfo.get_protocol(), name=name) + + self.add_test(GstValidateLaunchTest(fname, + self.test_manager.options, + self.test_manager.reporter, + pipeline_desc, + scenario=scenario, + media_descriptor=mediainfo) + ) + + +class GstValidatePlaybinTestsGenerator(GstValidatePipelineTestsGenerator): + + def __init__(self, test_manager): + GstValidatePipelineTestsGenerator.__init__( + self, "playback", test_manager, "playbin") + + def populate_tests(self, uri_minfo_special_scenarios, scenarios): + for uri, minfo, special_scenarios in uri_minfo_special_scenarios: + pipe = self._pipeline_template + protocol = minfo.media_descriptor.get_protocol() + + pipe += " uri=%s" % uri + + for scenario in special_scenarios + scenarios: + cpipe = pipe + if not minfo.media_descriptor.is_compatible(scenario): + continue + + if self.test_manager.options.mute: + if scenario.needs_clock_sync() or \ + minfo.media_descriptor.need_clock_sync(): + fakesink = "'fakesink sync=true'" + else: + fakesink = "'fakesink'" + + cpipe += " audio-sink=%s video-sink=%s" % ( + fakesink, fakesink) + + fname = "%s.%s" % (self.get_fname(scenario, + protocol), + os.path.basename(minfo.media_descriptor.get_clean_name())) + self.debug("Adding: %s", fname) + + if scenario.does_reverse_playback() and protocol == Protocols.HTTP: + # 10MB so we can reverse playback + cpipe += " ring-buffer-max-size=10485760" + + self.add_test(GstValidateLaunchTest(fname, + self.test_manager.options, + self.test_manager.reporter, + cpipe, + scenario=scenario, + media_descriptor=minfo.media_descriptor) + ) + + +class GstValidateMixerTestsGenerator(GstValidatePipelineTestsGenerator): + + def __init__(self, name, test_manager, mixer, media_type, converter="", + num_sources=3, mixed_srcs={}, valid_scenarios=[]): + pipe_template = "%(mixer)s name=_mixer ! " + \ + converter + " ! %(sink)s " + self.converter = converter + self.mixer = mixer + self.media_type = media_type + self.num_sources = num_sources + self.mixed_srcs = mixed_srcs + super( + GstValidateMixerTestsGenerator, self).__init__(name, test_manager, pipe_template, + valid_scenarios=valid_scenarios) + + def populate_tests(self, uri_minfo_special_scenarios, scenarios): + if self.test_manager.options.validate_uris: + return + + wanted_ressources = [] + for uri, minfo, special_scenarios in uri_minfo_special_scenarios: + protocol = minfo.media_descriptor.get_protocol() + if protocol == Protocols.FILE and \ + minfo.media_descriptor.get_num_tracks(self.media_type) > 0: + wanted_ressources.append((uri, minfo)) + + if not self.mixed_srcs: + if not wanted_ressources: + return + + for i in range(len(uri_minfo_special_scenarios) / self.num_sources): + srcs = [] + name = "" + for nsource in range(self.num_sources): + uri, minfo = wanted_ressources[i + nsource] + srcs.append( + "uridecodebin uri=%s ! %s" % (uri, self.converter)) + fname = os.path.basename(uri).replace(".", "_") + if not name: + name = fname + else: + name += "+%s" % fname + + self.mixed_srcs[name] = tuple(srcs) + + for name, srcs in self.mixed_srcs.iteritems(): + if isinstance(srcs, dict): + pipe_arguments = { + "mixer": self.mixer + " %s" % srcs["mixer_props"]} + srcs = srcs["sources"] + else: + pipe_arguments = {"mixer": self.mixer} + + for scenario in scenarios: + fname = self.get_fname(scenario, Protocols.FILE) + "." + fname += name + + self.debug("Adding: %s", fname) + + if self.test_manager.options.mute: + if scenario.needs_clock_sync(): + pipe_arguments["sink"] = "fakesink sync=true" + else: + pipe_arguments["sink"] = "'fakesink'" + else: + pipe_arguments["sink"] = "auto%ssink" % self.media_type + + pipe = self._pipeline_template % pipe_arguments + + for src in srcs: + pipe += "%s ! _mixer. " % src + + self.add_test(GstValidateLaunchTest(fname, + self.test_manager.options, + self.test_manager.reporter, + pipe, + scenario=scenario) + ) + + +class GstValidateLaunchTest(GstValidateTest): + + def __init__(self, classname, options, reporter, pipeline_desc, + timeout=DEFAULT_TIMEOUT, scenario=None, + media_descriptor=None, duration=0, hard_timeout=None, + extra_env_variables={}): + try: + timeout = GST_VALIDATE_PROTOCOL_TIMEOUTS[ + media_descriptor.get_protocol()] + except KeyError: + pass + except AttributeError: + pass + + if scenario: + duration = scenario.get_duration() + elif media_descriptor: + duration = media_descriptor.get_duration() / GST_SECOND + + super( + GstValidateLaunchTest, self).__init__(GST_VALIDATE_COMMAND, classname, + options, reporter, + duration=duration, + scenario=scenario, + timeout=timeout, + hard_timeout=hard_timeout, + extra_env_variables=extra_env_variables) + + self.pipeline_desc = pipeline_desc + self.media_descriptor = media_descriptor + + def build_arguments(self): + GstValidateTest.build_arguments(self) + self.add_arguments(self.pipeline_desc) + if self.media_descriptor is not None and self.media_descriptor.get_path(): + self.add_arguments( + "--set-media-info", self.media_descriptor.get_path()) + + +class GstValidateMediaCheckTest(GstValidateTest): + + def __init__(self, classname, options, reporter, media_descriptor, + uri, minfo_path, timeout=DEFAULT_TIMEOUT, extra_env_variables={}): + super( + GstValidateMediaCheckTest, self).__init__(G_V_DISCOVERER_COMMAND, classname, + options, reporter, + timeout=timeout, + extra_env_variables=extra_env_variables) + self._uri = uri + self.media_descriptor = media_descriptor + self._media_info_path = minfo_path + + def build_arguments(self): + Test.build_arguments(self) + self.add_arguments(self._uri, "--expected-results", + self._media_info_path) + + +class GstValidateTranscodingTest(GstValidateTest, GstValidateEncodingTestInterface): + scenarios_manager = ScenarioManager() + + def __init__(self, classname, options, reporter, + combination, uri, media_descriptor, + timeout=DEFAULT_TIMEOUT, + scenario=None, extra_env_variables={}): + + Loggable.__init__(self) + + file_dur = long(media_descriptor.get_duration()) / GST_SECOND + if not media_descriptor.get_num_tracks("video"): + self.debug("%s audio only file applying transcoding ratio." + "File 'duration' : %s" % (classname, file_dur)) + duration = file_dur / AUDIO_ONLY_FILE_TRANSCODING_RATIO + else: + duration = file_dur + + try: + timeout = GST_VALIDATE_PROTOCOL_TIMEOUTS[ + media_descriptor.get_protocol()] + except KeyError: + pass + + super( + GstValidateTranscodingTest, self).__init__(GST_VALIDATE_TRANSCODING_COMMAND, + classname, + options, + reporter, + duration=duration, + timeout=timeout, + scenario=scenario, + extra_env_variables=extra_env_variables) + + GstValidateEncodingTestInterface.__init__( + self, combination, media_descriptor) + + self.media_descriptor = media_descriptor + self.uri = uri + + def set_rendering_info(self): + self.dest_file = os.path.join(self.options.dest, + self.classname.replace(".transcode.", os.sep). + replace(".", os.sep)) + mkdir(os.path.dirname(urlparse.urlsplit(self.dest_file).path)) + if urlparse.urlparse(self.dest_file).scheme == "": + self.dest_file = path2url(self.dest_file) + + profile = self.get_profile() + self.add_arguments("-o", profile) + + def build_arguments(self): + GstValidateTest.build_arguments(self) + self.set_rendering_info() + self.add_arguments(self.uri, self.dest_file) + + def get_current_value(self): + if self.scenario: + sent_eos = self.sent_eos_position() + if sent_eos is not None: + t = time.time() + if ((t - sent_eos)) > 30: + if self.media_descriptor.get_protocol() == Protocols.HLS: + self.set_result(Result.PASSED, + """Got no EOS 30 seconds after sending EOS, + in HLS known and tolerated issue: + https://bugzilla.gnome.org/show_bug.cgi?id=723868""") + return Result.KNOWN_ERROR + + self.set_result( + Result.FAILED, "Pipeline did not stop 30 Seconds after sending EOS") + + return Result.FAILED + + size = self.get_current_size() + if size is None: + return self.get_current_position() + + return size + + def check_results(self): + if self.result in [Result.FAILED, Result.TIMEOUT] or \ + self.process.returncode != 0: + GstValidateTest.check_results(self) + return + + res, msg = self.check_encoded_file() + self.set_result(res, msg) + + +class GstValidateTestManager(GstValidateBaseTestManager): + + name = "validate" + + # List of all classes to create testsuites + GstValidateMediaCheckTestsGenerator = GstValidateMediaCheckTestsGenerator + GstValidateTranscodingTestsGenerator = GstValidateTranscodingTestsGenerator + GstValidatePipelineTestsGenerator = GstValidatePipelineTestsGenerator + GstValidatePlaybinTestsGenerator = GstValidatePlaybinTestsGenerator + GstValidateMixerTestsGenerator = GstValidateMixerTestsGenerator + GstValidateLaunchTest = GstValidateLaunchTest + GstValidateMediaCheckTest = GstValidateMediaCheckTest + GstValidateTranscodingTest = GstValidateTranscodingTest + + def __init__(self): + super(GstValidateTestManager, self).__init__() + self._uris = [] + self._run_defaults = True + self._is_populated = False + self._default_generators_registered = False + + def init(self): + if which(GST_VALIDATE_COMMAND) and which(GST_VALIDATE_TRANSCODING_COMMAND): + return True + return False + + def add_options(self, parser): + group = parser.add_argument_group("GstValidate tools specific options" + " and behaviours", + description="""When using --wanted-tests, all the scenarios can be used, even those which have +not been tested and explicitely activated if you set use --wanted-tests ALL""") + group.add_argument("--validate-check-uri", dest="validate_uris", + action="append", help="defines the uris to run default tests on") + + def print_valgrind_bugs(self): + # Look for all the 'pending' bugs in our supp file + bugs = [] + p = get_data_file('data', 'gstvalidate.supp') + with open(p) as f: + for l in f.readlines(): + l = l.strip() + if l.startswith('# PENDING:'): + tmp = l.split(' ') + bugs.append(tmp[2]) + + if bugs: + msg = "Ignored valgrind bugs:\n" + for b in bugs: + msg += " + %s\n" % b + printc(msg, Colors.FAIL, True) + + def populate_testsuite(self): + + if self._is_populated is True: + return + + if not self.options.config and not self.options.testsuites: + if self._run_defaults: + self.register_defaults() + else: + self.register_all() + + self._is_populated = True + + def list_tests(self): + if self.tests: + return self.tests + + if self._run_defaults: + scenarios = [self.scenarios_manager.get_scenario(scenario_name) + for scenario_name in self.get_scenarios()] + else: + scenarios = self.scenarios_manager.get_scenario(None) + uris = self._list_uris() + + for generator in self.get_generators(): + for test in generator.generate_tests(uris, scenarios): + self.add_test(test) + + return self.tests + + def _add_media(self, media_info, uri=None): + self.debug("Checking %s", media_info) + if isinstance(media_info, GstValidateMediaDescriptor): + media_descriptor = media_info + media_info = media_descriptor.get_path() + else: + media_descriptor = GstValidateMediaDescriptor(media_info) + + try: + # Just testing that the vairous mandatory infos are present + caps = media_descriptor.get_caps() + if uri is None: + uri = media_descriptor.get_uri() + + media_descriptor.set_protocol(urlparse.urlparse(uri).scheme) + for caps2, prot in GST_VALIDATE_CAPS_TO_PROTOCOL: + if caps2 == caps: + media_descriptor.set_protocol(prot) + break + + scenario_bname = media_descriptor.get_media_filepath() + special_scenarios = self.scenarios_manager.find_special_scenarios( + scenario_bname) + self._uris.append((uri, + NamedDic({"path": media_info, + "media_descriptor": media_descriptor}), + special_scenarios)) + except ConfigParser.NoOptionError as e: + self.debug("Exception: %s for %s", e, media_info) + + def _discover_file(self, uri, fpath): + try: + media_info = "%s.%s" % ( + fpath, GstValidateMediaDescriptor.MEDIA_INFO_EXT) + args = G_V_DISCOVERER_COMMAND.split(" ") + args.append(uri) + if os.path.isfile(media_info) and not self.options.update_media_info: + self._add_media(media_info, uri) + return True + elif fpath.endswith(GstValidateMediaDescriptor.STREAM_INFO_EXT): + self._add_media(fpath) + return True + elif not self.options.generate_info and not self.options.update_media_info and not self.options.validate_uris: + return True + elif self.options.update_media_info and not os.path.isfile(media_info): + return True + + media_descriptor = GstValidateMediaDescriptor.new_from_uri( + uri, True, + self.options.generate_info_full) + if media_descriptor: + self._add_media(media_descriptor, uri) + else: + self.warning("Could not get any descriptor for %s" % uri) + + return True + + except subprocess.CalledProcessError as e: + if self.options.generate_info: + printc("Result: Failed", Colors.FAIL) + else: + self.error("Exception: %s", e) + return False + + def _list_uris(self): + if self._uris: + return self._uris + + if self.options.validate_uris: + for uri in self.options.validate_uris: + self._discover_file(uri, uri) + return self._uris + + if not self.args: + if isinstance(self.options.paths, str): + self.options.paths = [os.path.join(self.options.paths)] + + for path in self.options.paths: + for root, dirs, files in os.walk(path): + for f in files: + fpath = os.path.join(path, root, f) + if os.path.isdir(fpath) or \ + fpath.endswith(GstValidateMediaDescriptor.MEDIA_INFO_EXT) or\ + fpath.endswith(ScenarioManager.FILE_EXTENSION): + continue + else: + self._discover_file(path2url(fpath), fpath) + + self.debug("Uris found: %s", self._uris) + + return self._uris + + def needs_http_server(self): + for test in self.list_tests(): + if self._is_test_wanted(test) and test.media_descriptor is not None: + protocol = test.media_descriptor.get_protocol() + uri = test.media_descriptor.get_uri() + + if protocol in [Protocols.HTTP, Protocols.HLS, Protocols.DASH] and \ + "127.0.0.1:%s" % (self.options.http_server_port) in uri: + return True + return False + + def set_settings(self, options, args, reporter): + if options.wanted_tests: + for i in range(len(options.wanted_tests)): + if "ALL" in options.wanted_tests[i]: + self._run_defaults = False + options.wanted_tests[ + i] = options.wanted_tests[i].replace("ALL", "") + try: + options.wanted_tests.remove("") + except ValueError: + pass + + if options.validate_uris: + self.check_testslist = False + + super(GstValidateTestManager, self).set_settings( + options, args, reporter) + + def register_defaults(self): + """ + Registers the defaults: + * Scenarios to be used + * Encoding formats to be used + * Blacklisted tests + * Test generators + """ + self.register_default_scenarios() + self.register_default_encoding_formats() + self.register_default_blacklist() + self.register_default_test_generators() + + def register_default_scenarios(self): + """ + Registers default test scenarios + """ + if self.options.long_limit != 0: + self.add_scenarios([ + "play_15s", + "reverse_playback", + "fast_forward", + "seek_forward", + "seek_backward", + "seek_with_stop", + "switch_audio_track", + "switch_audio_track_while_paused", + "switch_subtitle_track", + "switch_subtitle_track_while_paused", + "disable_subtitle_track_while_paused", + "change_state_intensive", + "scrub_forward_seeking"]) + else: + self.add_scenarios([ + "play_15s", + "reverse_playback", + "fast_forward", + "seek_forward", + "seek_backward", + "seek_with_stop", + "switch_audio_track", + "switch_audio_track_while_paused", + "switch_subtitle_track", + "switch_subtitle_track_while_paused", + "disable_subtitle_track_while_paused", + "change_state_intensive", + "scrub_forward_seeking"]) + + def register_default_encoding_formats(self): + """ + Registers default encoding formats + """ + self.add_encoding_formats([ + MediaFormatCombination("ogg", "vorbis", "theora"), + MediaFormatCombination("webm", "vorbis", "vp8"), + MediaFormatCombination("mp4", "mp3", "h264"), + MediaFormatCombination("mkv", "vorbis", "h264"), + ]) + + def register_default_blacklist(self): + self.set_default_blacklist([ + # hls known issues + ("validate.hls.playback.fast_forward.*", + "https://bugzilla.gnome.org/show_bug.cgi?id=698155"), + ("validate.hls.playback.seek_with_stop.*", + "https://bugzilla.gnome.org/show_bug.cgi?id=723268"), + ("validate.hls.playback.reverse_playback.*", + "https://bugzilla.gnome.org/show_bug.cgi?id=702595"), + ("validate.hls.*scrub_forward_seeking.*", + "https://bugzilla.gnome.org/show_bug.cgi?id=606382"), + ("validate.hls.*seek_backward.*", + "https://bugzilla.gnome.org/show_bug.cgi?id=606382"), + ("validate.hls.*seek_forward.*", + "https://bugzilla.gnome.org/show_bug.cgi?id=606382"), + + # Matroska/WEBM known issues: + ("validate.*.reverse_playback.*webm$", + "https://bugzilla.gnome.org/show_bug.cgi?id=679250"), + ("validate.*.reverse_playback.*mkv$", + "https://bugzilla.gnome.org/show_bug.cgi?id=679250"), + ("validate.http.playback.seek_with_stop.*webm", + "matroskademux.gst_matroska_demux_handle_seek_push: Seek end-time not supported in streaming mode"), + ("validate.http.playback.seek_with_stop.*mkv", + "matroskademux.gst_matroska_demux_handle_seek_push: Seek end-time not supported in streaming mode"), + + # MPEG TS known issues: + ('(?i)validate.*.playback.reverse_playback.*(?:_|.)(?:|m)ts$', + "https://bugzilla.gnome.org/show_bug.cgi?id=702595"), + + # HTTP known issues: + ("validate.http.*scrub_forward_seeking.*", + "This is not stable enough for now."), + ("validate.http.playback.change_state_intensive.raw_video_mov", + "This is not stable enough for now. (flow return from pad push doesn't match expected value)"), + + # MXF known issues" + (".*reverse_playback.*mxf", + "Reverse playback is not handled in MXF"), + ("validate\.file\.transcode.*mxf", + "FIXME: Transcoding and mixing tests need to be tested"), + + # Videomixing known issues + ("validate.file.*.simple.scrub_forward_seeking.synchronized", + "https://bugzilla.gnome.org/show_bug.cgi?id=734060"), + + # FLAC known issues" + (".*reverse_playback.*flac", + "Reverse playback is not handled in flac"), + + # WMV known issues" + (".*reverse_playback.*wmv", + "Reverse playback is not handled in wmv"), + (".*reverse_playback.*asf", + "Reverse playback is not handled in asf"), + ]) + + def register_default_test_generators(self): + """ + Registers default test generators + """ + if self._default_generators_registered: + return + + self.add_generators([GstValidatePlaybinTestsGenerator(self), + GstValidateMediaCheckTestsGenerator(self), + GstValidateTranscodingTestsGenerator(self)]) + self._default_generators_registered = True diff --git a/validate/launcher/baseclasses.py b/validate/launcher/baseclasses.py new file mode 100644 index 0000000..8bef580 --- /dev/null +++ b/validate/launcher/baseclasses.py @@ -0,0 +1,1719 @@ +#!/usr/bin/env python2 +# +# Copyright (c) 2013,Thibault Saunier +# +# This program 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.1 of the License, or (at your option) any later version. +# +# This program 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 program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301, USA. + +""" Class representing tests and test managers. """ + +import os +import sys +import re +import time +import utils +import signal +import urlparse +import subprocess +import threading +import Queue +import reporters +import ConfigParser +import loggable +from loggable import Loggable +import xml.etree.cElementTree as ET + +from utils import mkdir, Result, Colors, printc, DEFAULT_TIMEOUT, GST_SECOND, \ + Protocols, look_for_file_in_source_dir, get_data_file + +# The factor by which we increase the hard timeout when running inside +# Valgrind +VALGRIND_TIMEOUT_FACTOR = 20 +# The error reported by valgrind when detecting errors +VALGRIND_ERROR_CODE = 20 + + +class Test(Loggable): + + """ A class representing a particular test. """ + + def __init__(self, application_name, classname, options, + reporter, duration=0, timeout=DEFAULT_TIMEOUT, + hard_timeout=None, extra_env_variables={}): + """ + @timeout: The timeout during which the value return by get_current_value + keeps being exactly equal + @hard_timeout: Max time the test can take in absolute + """ + Loggable.__init__(self) + self.timeout = timeout + self.hard_timeout = hard_timeout + self.classname = classname + self.options = options + self.application = application_name + self.command = "" + self.reporter = reporter + self.process = None + self.proc_env = None + self.thread = None + self.queue = None + self.duration = duration + + self.extra_env_variables = extra_env_variables + + self.clean() + + def clean(self): + self.message = "" + self.error_str = "" + self.time_taken = 0.0 + self._starting_time = None + self.result = Result.NOT_RUN + self.logfile = None + self.out = None + self.extra_logfiles = [] + self.__env_variable = [] + + def __str__(self): + string = self.classname + if self.result != Result.NOT_RUN: + string += ": " + self.result + if self.result in [Result.FAILED, Result.TIMEOUT]: + string += " '%s'\n" \ + " You can reproduce with: %s %s\n" \ + % (self.message, self._env_variable, self.command) + + if not self.options.redirect_logs: + string += " You can find logs in:\n" \ + " - %s" % (self.logfile) + for log in self.extra_logfiles: + string += "\n - %s" % log + + return string + + def add_env_variable(self, variable, value=None): + """ + Only usefull so that the gst-validate-launcher can print the exact + right command line to reproduce the tests + """ + if value is None: + value = os.environ.get(variable, None) + + if value is None: + return + + self.__env_variable.append(variable) + + @property + def _env_variable(self): + res = "" + for var in set(self.__env_variable): + if res: + res += " " + value = self.proc_env.get(var, None) + if value: + res += "%s=%s" % (var, value) + + return res + + def open_logfile(self): + path = os.path.join(self.options.logsdir, + self.classname.replace(".", os.sep)) + mkdir(os.path.dirname(path)) + self.logfile = path + + if self.options.redirect_logs == 'stdout': + self.out = sys.stdout + elif self.options.redirect_logs == 'stderr': + self.out = sys.stderr + else: + self.out = open(path, 'w+') + + def close_logfile(self): + if not self.options.redirect_logs: + self.out.close() + + self.out = None + + def _get_file_content(self, file_name): + f = open(file_name, 'r+') + value = f.read() + f.close() + + return value + + def get_log_content(self): + return self._get_file_content(self.logfile) + + def get_extra_log_content(self, extralog): + if extralog not in self.extra_logfiles: + return "" + + return self._get_file_content(extralog) + + def get_classname(self): + name = self.classname.split('.')[-1] + classname = self.classname.replace('.%s' % name, '') + + return classname + + def get_name(self): + return self.classname.split('.')[-1] + + def add_arguments(self, *args): + for arg in args: + self.command += " " + arg + + def build_arguments(self): + self.add_env_variable("LD_PRELOAD") + self.add_env_variable("DISPLAY") + + def set_result(self, result, message="", error=""): + self.debug("Setting result: %s (message: %s, error: %s)" % (result, + message, error)) + if result is Result.TIMEOUT and self.options.debug is True: + pname = subprocess.check_output(("readlink -e /proc/%s/exe" + % self.process.pid).split(' ')).replace('\n', '') + raw_input("%sTimeout happened you can attach gdb doing: $gdb %s %d%s\n" + "Press enter to continue" % (Colors.FAIL, pname, self.process.pid, + Colors.ENDC)) + + self.result = result + self.message = message + self.error_str = error + + def check_results(self): + if self.result is Result.FAILED or self.result is Result.TIMEOUT: + return + + self.debug("%s returncode: %s", self, self.process.returncode) + if self.process.returncode == 0: + self.set_result(Result.PASSED) + elif self.process.returncode == VALGRIND_ERROR_CODE: + self.set_result(Result.FAILED, "Valgrind reported errors") + else: + self.set_result(Result.FAILED, + "Application returned %d" % (self.process.returncode)) + + def get_current_value(self): + """ + Lets subclasses implement a nicer timeout measurement method + They should return some value with which we will compare + the previous and timeout if they are egual during self.timeout + seconds + """ + return Result.NOT_RUN + + def process_update(self): + """ + Returns True when process has finished running or has timed out. + """ + + if self.process is None: + # Process has not started running yet + return False + + self.process.poll() + if self.process.returncode is not None: + return True + + val = self.get_current_value() + + self.debug("Got value: %s" % val) + if val is Result.NOT_RUN: + # The get_current_value logic is not implemented... dumb + # timeout + if time.time() - self.last_change_ts > self.timeout: + self.set_result(Result.TIMEOUT, + "Application timed out: %s secs" % + self.timeout, + "timeout") + return True + return False + elif val is Result.FAILED: + return True + elif val is Result.KNOWN_ERROR: + return True + + self.log("New val %s" % val) + + if val == self.last_val: + delta = time.time() - self.last_change_ts + self.debug("%s: Same value for %d/%d seconds" % + (self, delta, self.timeout)) + if delta > self.timeout: + self.set_result(Result.TIMEOUT, + "Application timed out: %s secs" % + self.timeout, + "timeout") + return True + elif self.hard_timeout and time.time() - self.start_ts > self.hard_timeout: + self.set_result( + Result.TIMEOUT, "Hard timeout reached: %d secs" % self.hard_timeout) + return True + else: + self.last_change_ts = time.time() + self.last_val = val + + return False + + def get_subproc_env(self): + return os.environ + + def kill_subprocess(self): + if self.process is None: + return + + stime = time.time() + res = self.process.poll() + while res is None: + try: + self.debug("Subprocess is still alive, sending KILL signal") + self.process.send_signal(signal.SIGKILL) + time.sleep(1) + except OSError: + pass + if time.time() - stime > DEFAULT_TIMEOUT: + raise RuntimeError("Could not kill subprocess after %s second" + " Something is really wrong, => EXITING" + % DEFAULT_TIMEOUT) + res = self.process.poll() + + def thread_wrapper(self): + self.process = subprocess.Popen("exec " + self.command, + stderr=self.out, + stdout=self.out, + shell=True, + env=self.proc_env) + self.process.wait() + if self.result is not Result.TIMEOUT: + self.queue.put(None) + + def get_valgrind_suppressions(self): + return [self.get_valgrind_suppression_file('data', 'gstvalidate.supp')] + + def use_valgrind(self): + vglogsfile = self.logfile + '.valgrind' + self.extra_logfiles.append(vglogsfile) + + vg_args = [ + ('trace-children', 'yes'), + ('tool', 'memcheck'), + ('leak-check', 'full'), + ('leak-resolution', 'high'), + ('num-callers', '20'), + ('log-file', vglogsfile), + ('error-exitcode', str(VALGRIND_ERROR_CODE)), + ] + + for supp in self.get_valgrind_suppressions(): + vg_args.append(('suppressions', supp)) + + self.command = "valgrind %s %s" % (' '.join(map(lambda x: '--%s=%s' % (x[0], x[1]), vg_args)), + self.command) + + # Tune GLib's memory allocator to be more valgrind friendly + self.proc_env['G_DEBUG'] = 'gc-friendly' + self.add_env_variable('G_DEBUG', 'gc-friendly') + + self.proc_env['G_SLICE'] = 'always-malloc' + self.add_env_variable('G_SLICE', 'always-malloc') + + if self.hard_timeout is not None: + self.hard_timeout *= VALGRIND_TIMEOUT_FACTOR + self.timeout *= VALGRIND_TIMEOUT_FACTOR + + # Enable 'valgrind.config' + vg_config = get_data_file('data', 'valgrind.config') + + if self.proc_env.get('GST_VALIDATE_CONFIG'): + self.proc_env['GST_VALIDATE_CONFIG'] = '%s%s%s' % (self.proc_env['GST_VALIDATE_CONFIG'], os.pathsep, vg_config) + else: + self.proc_env['GST_VALIDATE_CONFIG'] = vg_config + + self.add_env_variable('GST_VALIDATE_CONFIG', self.proc_env['GST_VALIDATE_CONFIG']) + + def test_start(self, queue): + self.open_logfile() + + self.queue = queue + self.command = "%s " % (self.application) + self._starting_time = time.time() + self.build_arguments() + self.proc_env = self.get_subproc_env() + + for var, value in self.extra_env_variables.items(): + self.proc_env[var] = self.proc_env.get(var, '') + os.pathsep + value + self.add_env_variable(var, self.proc_env[var]) + + if self.options.valgrind: + self.use_valgrind() + + message = "Launching: %s%s\n" \ + " Command: '%s %s'\n" % (Colors.ENDC, self.classname, + self._env_variable, self.command) + if not self.options.redirect_logs: + message += " Logs:\n" \ + " - %s" % (self.logfile) + for log in self.extra_logfiles: + message += "\n - %s" % log + + self.out.write("=================\n" + "Test name: %s\n" + "Command: '%s'\n" + "=================\n\n" + % (self.classname, self.command)) + self.out.flush() + + printc(message, Colors.OKBLUE) + + self.thread = threading.Thread(target=self.thread_wrapper) + self.thread.start() + + self.last_val = 0 + self.last_change_ts = time.time() + self.start_ts = time.time() + + def test_end(self): + self.kill_subprocess() + self.thread.join() + self.time_taken = time.time() - self._starting_time + + printc("%s: %s%s\n" % (self.classname, self.result, + " (" + self.message + ")" if self.message else ""), + color=utils.get_color_for_result(self.result)) + + self.close_logfile() + + return self.result + + +class GstValidateTest(Test): + + """ A class representing a particular test. """ + findpos_regex = re.compile( + '.*position.*(\d+):(\d+):(\d+).(\d+).*duration.*(\d+):(\d+):(\d+).(\d+)') + findlastseek_regex = re.compile( + 'seeking to.*(\d+):(\d+):(\d+).(\d+).*stop.*(\d+):(\d+):(\d+).(\d+).*rate.*(\d+)\.(\d+)') + + HARD_TIMEOUT_FACTOR = 5 + + def __init__(self, application_name, classname, + options, reporter, duration=0, + timeout=DEFAULT_TIMEOUT, scenario=None, hard_timeout=None, + extra_env_variables={}): + + if not hard_timeout and self.HARD_TIMEOUT_FACTOR: + if timeout: + hard_timeout = timeout * self.HARD_TIMEOUT_FACTOR + elif duration: + hard_timeout = duration * self.HARD_TIMEOUT_FACTOR + else: + hard_timeout = None + + # If we are running from source, use the -debug version of the + # application which is using rpath instead of libtool's wrappers. It's + # slightly faster to start and will not confuse valgrind. + debug = '%s-debug' % application_name + p = look_for_file_in_source_dir('tools', debug) + if p: + application_name = p + + super(GstValidateTest, self).__init__(application_name, classname, + options, reporter, + duration=duration, + timeout=timeout, + hard_timeout=hard_timeout, + extra_env_variables=extra_env_variables) + + # defines how much the process can be outside of the configured + # segment / seek + self._sent_eos_pos = None + + self.validatelogs = None + if scenario is None or scenario.name.lower() == "none": + self.scenario = None + else: + self.scenario = scenario + + def get_current_value(self): + if self.scenario: + sent_eos = self.sent_eos_position() + if sent_eos is not None: + t = time.time() + if ((t - sent_eos)) > 30: + if self.media_descriptor.get_protocol() == Protocols.HLS: + self.set_result(Result.PASSED, + """Got no EOS 30 seconds after sending EOS, + in HLS known and tolerated issue: + https://bugzilla.gnome.org/show_bug.cgi?id=723868""") + return Result.KNOWN_ERROR + + self.set_result( + Result.FAILED, "Pipeline did not stop 30 Seconds after sending EOS") + + return Result.FAILED + + return self.get_current_position() + + def get_subproc_env(self): + self.validatelogs = self.logfile + '.validate.logs' + logfiles = self.validatelogs + if self.options.redirect_logs: + logfiles += os.pathsep + \ + self.options.redirect_logs.replace("<", '').replace(">", '') + + subproc_env = os.environ.copy() + + utils.touch(self.validatelogs) + subproc_env["GST_VALIDATE_FILE"] = logfiles + self.extra_logfiles.append(self.validatelogs) + + if 'GST_DEBUG' in os.environ and not self.options.redirect_logs: + gstlogsfile = self.logfile + '.gstdebug' + self.extra_logfiles.append(gstlogsfile) + subproc_env["GST_DEBUG_FILE"] = gstlogsfile + + if self.options.no_color: + subproc_env["GST_DEBUG_NO_COLOR"] = '1' + + # Ensure XInitThreads is called, see bgo#731525 + subproc_env['GST_GL_XINITTHREADS'] = '1' + self.add_env_variable('GST_GL_XINITTHREADS', '1') + + if self.scenario is not None: + scenario = self.scenario.get_execution_name() + if self.options.valgrind: + # Increase sink's max-lateness property when running inside + # Valgrind as it slows down everything quiet a lot. + scenario = "setup_sink_props_max_lateness:%s" % scenario + + subproc_env["GST_VALIDATE_SCENARIO"] = scenario + self.add_env_variable("GST_VALIDATE_SCENARIO", + subproc_env["GST_VALIDATE_SCENARIO"]) + else: + try: + del subproc_env["GST_VALIDATE_SCENARIO"] + except KeyError: + pass + + return subproc_env + + def clean(self): + Test.clean(self) + self._sent_eos_pos = None + + def build_arguments(self): + super(GstValidateTest, self).build_arguments() + if "GST_VALIDATE" in os.environ: + self.add_env_variable("GST_VALIDATE", os.environ["GST_VALIDATE"]) + + if "GST_VALIDATE_SCENARIOS_PATH" in os.environ: + self.add_env_variable("GST_VALIDATE_SCENARIOS_PATH", + os.environ["GST_VALIDATE_SCENARIOS_PATH"]) + + self.add_env_variable("GST_VALIDATE_CONFIG") + self.add_env_variable("GST_VALIDATE_OVERRIDE") + + def get_extra_log_content(self, extralog): + value = Test.get_extra_log_content(self, extralog) + + if extralog == self.validatelogs: + value = re.sub("\r", "", value) + + return value + + def get_validate_criticals_errors(self): + ret = "[" + errors = [] + for l in open(self.validatelogs, 'r').readlines(): + if "critical : " in l: + error = l.split("critical : ")[1].replace("\n", '') + if error not in errors: + if ret != "[": + ret += ", " + ret += error + errors.append(error) + + if ret == "[": + return None + else: + return ret + "]" + + def check_results(self): + if self.result is Result.FAILED or self.result is Result.PASSED or self.result is Result.TIMEOUT: + return + + self.debug("%s returncode: %s", self, self.process.returncode) + + criticals = self.get_validate_criticals_errors() + if self.process.returncode == 139: + # FIXME Reimplement something like that if needed + # self.get_backtrace("SEGFAULT") + self.set_result(Result.FAILED, + "Application segfaulted", + "segfault") + elif self.process.returncode == VALGRIND_ERROR_CODE: + self.set_result(Result.FAILED, "Valgrind reported errors") + elif criticals or self.process.returncode != 0: + if criticals is None: + criticals = "No criticals" + self.set_result(Result.FAILED, + "Application returned %s (issues: %s)" + % (self.process.returncode, criticals)) + else: + self.set_result(Result.PASSED) + + def _parse_position(self, p): + self.log("Parsing %s" % p) + times = self.findpos_regex.findall(p) + + if len(times) != 1: + self.warning("Got a unparsable value: %s" % p) + return 0, 0 + + return (utils.gsttime_from_tuple(times[0][:4]), + utils.gsttime_from_tuple(times[0][4:])) + + def _parse_buffering(self, b): + return b.split("buffering... ")[1].split("%")[0], 100 + + def _get_position(self): + position = duration = -1 + + self.debug("Getting position") + m = None + for l in reversed(open(self.validatelogs, 'r').readlines()): + l = l.lower() + if ""): + position, duration = self._parse_position(j) + elif j.startswith("buffering") and j.endswith("%"): + position, duration = self._parse_buffering(j) + else: + self.log("No info in %s" % j) + + return position, duration + + def _get_last_seek_values(self): + m = None + rate = start = stop = None + + for l in reversed(open(self.validatelogs, 'r').readlines()): + l = l.lower() + if "seeking to: " in l: + m = l + break + + if m is None: + self.debug("Could not fine any seeking info") + return start, stop, rate + + values = self.findlastseek_regex.findall(m) + if len(values) != 1: + self.warning("Got an unparsable seek value %s", m) + return start, stop, rate + + v = values[0] + return (utils.gsttime_from_tuple(v[:4]), + utils.gsttime_from_tuple(v[4:8]), + float(str(v[8]) + "." + str(v[9]))) + + def sent_eos_position(self): + if self._sent_eos_pos is not None: + return self._sent_eos_pos + + for l in reversed(open(self.validatelogs, 'r').readlines()): + l = l.lower() + if "sending eos" in l: + self._sent_eos_pos = time.time() + return self._sent_eos_pos + + return None + + def get_current_position(self): + position, duration = self._get_position() + if position == -1: + return position + + return position + + def get_valgrind_suppression_file(self, subdir, name): + p = get_data_file(subdir, name) + if p: + return p + + self.error("Could not find any %s file" % name) + + def get_valgrind_suppressions(self): + result = super(GstValidateTest, self).get_valgrind_suppressions() + return result + [self.get_valgrind_suppression_file('common', 'gst.supp')] + + +class GstValidateEncodingTestInterface(object): + DURATION_TOLERANCE = GST_SECOND / 4 + + def __init__(self, combination, media_descriptor, duration_tolerance=None): + super(GstValidateEncodingTestInterface, self).__init__() + + self.media_descriptor = media_descriptor + self.combination = combination + self.dest_file = "" + + self._duration_tolerance = duration_tolerance + if duration_tolerance is None: + self._duration_tolerance = self.DURATION_TOLERANCE + + def get_current_size(self): + try: + size = os.stat(urlparse.urlparse(self.dest_file).path).st_size + except OSError: + return None + + self.debug("Size: %s" % size) + return size + + def _get_profile_full(self, muxer, venc, aenc, video_restriction=None, + audio_restriction=None, audio_presence=0, + video_presence=0): + ret = "\"" + if muxer: + ret += muxer + ret += ":" + if venc: + if video_restriction is not None: + ret = ret + video_restriction + '->' + ret += venc + if video_presence: + ret = ret + '|' + str(video_presence) + if aenc: + ret += ":" + if audio_restriction is not None: + ret = ret + audio_restriction + '->' + ret += aenc + if audio_presence: + ret = ret + '|' + str(audio_presence) + + ret += "\"" + return ret.replace("::", ":") + + def get_profile(self, video_restriction=None, audio_restriction=None): + vcaps = self.combination.get_video_caps() + acaps = self.combination.get_audio_caps() + if self.media_descriptor is not None: + if self.media_descriptor.get_num_tracks("video") == 0: + vcaps = None + + if self.media_descriptor.get_num_tracks("audio") == 0: + acaps = None + + return self._get_profile_full(self.combination.get_muxer_caps(), + vcaps, acaps, + video_restriction=video_restriction, + audio_restriction=audio_restriction) + + def _clean_caps(self, caps): + """ + Returns a list of key=value or structure name, without "(types)" or ";" or "," + """ + return re.sub(r"\(.+?\)\s*| |;", '', caps).split(',') + + def _has_caps_type_variant(self, c, ccaps): + """ + Handle situations where we can have application/ogg or video/ogg or + audio/ogg + """ + has_variant = False + media_type = re.findall("application/|video/|audio/", c) + if media_type: + media_type = media_type[0].replace('/', '') + possible_mtypes = ["application", "video", "audio"] + possible_mtypes.remove(media_type) + for tmptype in possible_mtypes: + possible_c_variant = c.replace(media_type, tmptype) + if possible_c_variant in ccaps: + self.info( + "Found %s in %s, good enough!", possible_c_variant, ccaps) + has_variant = True + + return has_variant + + def check_encoded_file(self): + result_descriptor = GstValidateMediaDescriptor.new_from_uri( + self.dest_file) + if result_descriptor is None: + return (Result.FAILED, "Could not discover encoded file %s" + % self.dest_file) + + duration = result_descriptor.get_duration() + orig_duration = self.media_descriptor.get_duration() + tolerance = self._duration_tolerance + + if orig_duration - tolerance >= duration <= orig_duration + tolerance: + os.remove(result_descriptor.get_path()) + return (Result.FAILED, "Duration of encoded file is " + " wrong (%s instead of %s)" % + (utils.TIME_ARGS(duration), + utils.TIME_ARGS(orig_duration))) + else: + all_tracks_caps = result_descriptor.get_tracks_caps() + container_caps = result_descriptor.get_caps() + if container_caps: + all_tracks_caps.insert(0, ("container", container_caps)) + + for track_type, caps in all_tracks_caps: + ccaps = self._clean_caps(caps) + wanted_caps = self.combination.get_caps(track_type) + cwanted_caps = self._clean_caps(wanted_caps) + + if wanted_caps is None: + os.remove(result_descriptor.get_path()) + return (Result.FAILED, + "Found a track of type %s in the encoded files" + " but none where wanted in the encoded profile: %s" + % (track_type, self.combination)) + + for c in cwanted_caps: + if c not in ccaps: + if not self._has_caps_type_variant(c, ccaps): + os.remove(result_descriptor.get_path()) + return (Result.FAILED, + "Field: %s (from %s) not in caps of the outputed file %s" + % (wanted_caps, c, ccaps)) + + os.remove(result_descriptor.get_path()) + return (Result.PASSED, "") + + +class TestsManager(Loggable): + + """ A class responsible for managing tests. """ + + name = "" + + def __init__(self): + + Loggable.__init__(self) + + self.tests = [] + self.unwanted_tests = [] + self.options = None + self.args = None + self.reporter = None + self.wanted_tests_patterns = [] + self.blacklisted_tests_patterns = [] + self._generators = [] + self.queue = Queue.Queue() + self.jobs = [] + self.total_num_tests = 0 + self.starting_test_num = 0 + self.check_testslist = True + self.all_tests = None + + def init(self): + return False + + def list_tests(self): + return sorted(list(self.tests)) + + def add_test(self, test): + if self._is_test_wanted(test): + if test not in self.tests: + self.tests.append(test) + self.tests.sort(key=lambda test: test.classname) + else: + if test not in self.tests: + self.unwanted_tests.append(test) + self.unwanted_tests.sort(key=lambda test: test.classname) + + def get_tests(self): + return self.tests + + def populate_testsuite(self): + pass + + def add_generators(self, generators): + """ + @generators: A list of, or one single #TestsGenerator to be used to generate tests + """ + if isinstance(generators, list): + self._generators.extend(generators) + else: + self._generators.append(generators) + + self._generators = list(set(self._generators)) + + def get_generators(self): + return self._generators + + def _add_blacklist(self, blacklisted_tests): + if not isinstance(blacklisted_tests, list): + blacklisted_tests = [blacklisted_tests] + + for patterns in blacklisted_tests: + for pattern in patterns.split(","): + self.blacklisted_tests_patterns.append(re.compile(pattern)) + + def set_default_blacklist(self, default_blacklist): + msg = "\nCurrently 'hardcoded' %s blacklisted tests:\n\n" % self.name + for name, bug in default_blacklist: + self._add_blacklist(name) + msg += " + %s \n --> bug: %s\n" % (name, bug) + + printc(msg, Colors.FAIL, True) + + def add_options(self, parser): + """ Add more arguments. """ + pass + + def set_settings(self, options, args, reporter): + """ Set properties after options parsing. """ + self.options = options + self.args = args + self.reporter = reporter + + self.populate_testsuite() + + if self.options.valgrind: + self.print_valgrind_bugs() + + if options.wanted_tests: + for patterns in options.wanted_tests: + for pattern in patterns.split(","): + self.wanted_tests_patterns.append(re.compile(pattern)) + + if options.blacklisted_tests: + for patterns in options.blacklisted_tests: + self._add_blacklist(patterns) + + def _check_blacklisted(self, test): + for pattern in self.blacklisted_tests_patterns: + if pattern.findall(test.classname): + return True + + return False + + def _is_test_wanted(self, test): + if self._check_blacklisted(test): + return False + + if test.duration > 0 and int(self.options.long_limit) < int(test.duration): + self.info("Not activating %s as its duration (%d) is superior" + " than the long limit (%d)" % (test, test.duration, + int(self.options.long_limit))) + return False + + if not self.wanted_tests_patterns: + return True + + for pattern in self.wanted_tests_patterns: + if pattern.findall(test.classname): + return True + + return False + + def test_wait(self): + while True: + # Check process every second for timeout + try: + self.queue.get(timeout=1) + except Queue.Empty: + pass + + for test in self.jobs: + if test.process_update(): + self.jobs.remove(test) + return test + + def tests_wait(self): + try: + test = self.test_wait() + test.check_results() + except KeyboardInterrupt: + for test in self.jobs: + test.kill_subprocess() + raise + + return test + + def start_new_job(self, tests_left): + try: + test = tests_left.pop(0) + except IndexError: + return False + + self.print_test_num(test) + test.test_start(self.queue) + + self.jobs.append(test) + + return True + + def run_tests(self, starting_test_num, total_num_tests): + self.total_num_tests = total_num_tests + self.starting_test_num = starting_test_num + + num_jobs = min(self.options.num_jobs, len(self.tests)) + tests_left = list(self.tests) + jobs_running = 0 + + for i in range(num_jobs): + if not self.start_new_job(tests_left): + break + jobs_running += 1 + + while jobs_running != 0: + test = self.tests_wait() + jobs_running -= 1 + self.print_test_num(test) + res = test.test_end() + self.reporter.after_test(test) + if res != Result.PASSED and (self.options.forever or + self.options.fatal_error): + return test.result + if self.start_new_job(tests_left): + jobs_running += 1 + + return Result.PASSED + + def print_test_num(self, test): + cur_test_num = self.starting_test_num + self.tests.index(test) + 1 + sys.stdout.write("[%d / %d] " % (cur_test_num, self.total_num_tests)) + + def clean_tests(self): + for test in self.tests: + test.clean() + + def needs_http_server(self): + return False + + def print_valgrind_bugs(self): + pass + + +class TestsGenerator(Loggable): + + def __init__(self, name, test_manager, tests=[]): + Loggable.__init__(self) + self.name = name + self.test_manager = test_manager + self._tests = {} + for test in tests: + self._tests[test.classname] = test + + def generate_tests(self, *kwargs): + """ + Method that generates tests + """ + return list(self._tests.values()) + + def add_test(self, test): + self._tests[test.classname] = test + + +class GstValidateTestsGenerator(TestsGenerator): + + def populate_tests(self, uri_minfo_special_scenarios, scenarios): + pass + + def generate_tests(self, uri_minfo_special_scenarios, scenarios): + self.populate_tests(uri_minfo_special_scenarios, scenarios) + return super(GstValidateTestsGenerator, self).generate_tests() + + +class _TestsLauncher(Loggable): + + def __init__(self, libsdir): + + Loggable.__init__(self) + + self.libsdir = libsdir + self.options = None + self.testers = [] + self.tests = [] + self.reporter = None + self._list_testers() + self.all_tests = None + self.wanted_tests_patterns = [] + + def _list_app_dirs(self): + app_dirs = [] + app_dirs.append(os.path.join(self.libsdir, "apps")) + env_dirs = os.environ.get("GST_VALIDATE_APPS_DIR") + if env_dirs is not None: + for dir_ in env_dirs.split(":"): + app_dirs.append(dir_) + sys.path.append(dir_) + + return app_dirs + + def _exec_app(self, app_dir, env): + try: + files = os.listdir(app_dir) + except OSError as e: + self.debug("Could not list %s: %s" % (app_dir, e)) + files = [] + for f in files: + if f.endswith(".py"): + execfile(os.path.join(app_dir, f), env) + + def _exec_apps(self, env): + app_dirs = self._list_app_dirs() + for app_dir in app_dirs: + self._exec_app(app_dir, env) + + def _list_testers(self): + env = globals().copy() + self._exec_apps(env) + + testers = [i() for i in utils.get_subclasses(TestsManager, env)] + for tester in testers: + if tester.init() is True: + self.testers.append(tester) + else: + self.warning("Can not init tester: %s -- PATH is %s" + % (tester.name, os.environ["PATH"])) + + def add_options(self, parser): + for tester in self.testers: + tester.add_options(parser) + + def _load_testsuites(self): + testsuites = [] + for testsuite in self.options.testsuites: + if not os.path.isabs(testsuite): + testsuite = os.path.join(self.options.testsuites_dir, testsuite + ".py") + + try: + sys.path.insert(0, os.path.dirname(testsuite)) + module = __import__(os.path.basename(testsuite).replace(".py", "")) + except Exception as e: + printc("Could not load testsuite: %s, reason: %s" + % (testsuite, e), Colors.FAIL) + continue + finally: + sys.path.remove(os.path.dirname(testsuite)) + + testsuites.append(module) + if not hasattr(module, "TEST_MANAGER"): + module.TEST_MANAGER = [tester.name for tester in self.testers] + elif not isinstance(module.TEST_MANAGER, list): + module.TEST_MANAGER = [module.TEST_MANAGER] + + self.options.testsuites = testsuites + + def _setup_testsuites(self): + for testsuite in self.options.testsuites: + loaded = False + wanted_test_manager = None + if hasattr(testsuite, "TEST_MANAGER"): + wanted_test_manager = testsuite.TEST_MANAGER + if not isinstance(wanted_test_manager, list): + wanted_test_manager = [wanted_test_manager] + + for tester in self.testers: + if wanted_test_manager is not None and \ + tester.name not in wanted_test_manager: + continue + + if testsuite.setup_tests(tester, self.options): + loaded = True + + if not loaded: + printc("Could not load testsuite: %s" + " maybe because of missing TestManager" + % (testsuite), Colors.FAIL) + + def _load_config(self, options): + printc("Loading config files is DEPRECATED" + " you should use the new testsuite format now",) + + for tester in self.testers: + tester.options = options + globals()[tester.name] = tester + globals()["options"] = options + c__file__ = __file__ + globals()["__file__"] = self.options.config + execfile(self.options.config, globals()) + globals()["__file__"] = c__file__ + + def set_settings(self, options, args): + self.reporter = reporters.XunitReporter(options) + + self.options = options + wanted_testers = None + for tester in self.testers: + if tester.name in args: + wanted_testers = tester.name + + if wanted_testers: + testers = self.testers + self.testers = [] + for tester in testers: + if tester.name in args: + self.testers.append(tester) + args.remove(tester.name) + + if options.config: + self._load_config(options) + + self._load_testsuites() + + for tester in self.testers: + tester.set_settings(options, args, self.reporter) + + if not options.config and options.testsuites: + self._setup_testsuites() + + def _check_tester_has_other_testsuite(self, testsuite, tester): + if len(testsuite.TEST_MANAGER) > 1: + return True + + if tester.name != testsuite.TEST_MANAGER[0]: + return True + + for t in self.options.testsuites: + if t != testsuite: + for other_testmanager in testsuite.TEST_MANAGER: + if other_testmanager == tester.name: + return True + + return False + + def _check_defined_tests(self, tester, tests): + if self.options.blacklisted_tests or self.options.wanted_tests: + return + + tests_names = [test.classname for test in tests] + for testsuite in self.options.testsuites: + if not self._check_tester_has_other_testsuite(testsuite, tester) \ + and tester.check_testslist: + try: + testlist_file = open(os.path.splitext(testsuite.__file__)[0] + ".testslist", + 'rw') + + know_tests = testlist_file.read().split("\n") + testlist_file.close() + + testlist_file = open(os.path.splitext(testsuite.__file__)[0] + ".testslist", + 'w') + except IOError: + return + + for test in know_tests: + if test and test not in tests_names: + printc("Test %s Not in testsuite %s anymore" + % (test, testsuite.__file__), Colors.FAIL) + + for test in tests_names: + testlist_file.write("%s\n" % test) + if test and test not in know_tests: + printc("Test %s is NEW in testsuite %s" + % (test, testsuite.__file__), Colors.OKGREEN) + + testlist_file.close() + return + + def list_tests(self): + for tester in self.testers: + tests = tester.list_tests() + self._check_defined_tests(tester, tests) + self.tests.extend(tests) + return sorted(list(self.tests)) + + def _run_tests(self): + cur_test_num = 0 + + if not self.all_tests: + total_num_tests = 1 + self.all_tests = [] + for tester in self.testers: + self.all_tests.extend(tester.list_tests()) + total_num_tests = len(self.all_tests) + + self.reporter.init_timer() + for tester in self.testers: + res = tester.run_tests(cur_test_num, total_num_tests) + cur_test_num += len(tester.list_tests()) + if res != Result.PASSED and (self.options.forever or + self.options.fatal_error): + return False + + return True + + def _clean_tests(self): + for tester in self.testers: + tester.clean_tests() + + def run_tests(self): + if self.options.forever: + while self._run_tests(): + self._clean_tests() + + return False + else: + return self._run_tests() + + def final_report(self): + self.reporter.final_report() + + def needs_http_server(self): + for tester in self.testers: + if tester.needs_http_server(): + return True + + +class NamedDic(object): + + def __init__(self, props): + if props: + for name, value in props.iteritems(): + setattr(self, name, value) + + +class Scenario(object): + + def __init__(self, name, props, path=None): + self.name = name + self.path = path + + for prop, value in props: + setattr(self, prop.replace("-", "_"), value) + + def get_execution_name(self): + if self.path is not None: + return self.path + else: + return self.name + + def seeks(self): + if hasattr(self, "seek"): + return bool(self.seek) + + return False + + def needs_clock_sync(self): + if hasattr(self, "need_clock_sync"): + return bool(self.need_clock_sync) + + return False + + def get_min_media_duration(self): + if hasattr(self, "min_media_duration"): + return long(self.min_media_duration) + + return 0 + + def does_reverse_playback(self): + if hasattr(self, "reverse_playback"): + return bool(self.seek) + + return False + + def get_duration(self): + try: + return float(getattr(self, "duration")) + except AttributeError: + return 0 + + def get_min_tracks(self, track_type): + try: + return int(getattr(self, "min_%s_track" % track_type)) + except AttributeError: + return 0 + + +class ScenarioManager(Loggable): + _instance = None + all_scenarios = [] + + FILE_EXTENSION = "scenario" + GST_VALIDATE_COMMAND = "gst-validate-1.0" + if "win32" in sys.platform: + GST_VALIDATE_COMMAND += ".exe" + + def __new__(cls, *args, **kwargs): + if not cls._instance: + cls._instance = super(ScenarioManager, cls).__new__( + cls, *args, **kwargs) + cls._instance.config = None + cls._instance.discovered = False + Loggable.__init__(cls._instance) + + return cls._instance + + def find_special_scenarios(self, mfile): + scenarios = [] + mfile_bname = os.path.basename(mfile) + + for f in os.listdir(os.path.dirname(mfile)): + if re.findall("%s\..*\.%s$" % (mfile_bname, self.FILE_EXTENSION), f): + scenarios.append(os.path.join(os.path.dirname(mfile), f)) + + if scenarios: + scenarios = self.discover_scenarios(scenarios, mfile) + + return scenarios + + def discover_scenarios(self, scenario_paths=[], mfile=None): + """ + Discover scenarios specified in scenario_paths or the default ones + if nothing specified there + """ + scenarios = [] + scenario_defs = os.path.join(self.config.main_dir, "scenarios.def") + logs = open(os.path.join(self.config.logsdir, + "scenarios_discovery.log"), 'w') + + try: + command = [self.GST_VALIDATE_COMMAND, + "--scenarios-defs-output-file", scenario_defs] + command.extend(scenario_paths) + subprocess.check_call(command, stdout=logs, stderr=logs) + except subprocess.CalledProcessError: + pass + + config = ConfigParser.ConfigParser() + f = open(scenario_defs) + config.readfp(f) + + for section in config.sections(): + if scenario_paths: + for scenario_path in scenario_paths: + if mfile is None: + name = section + path = scenario_path + elif section in scenario_path: + # The real name of the scenario is: + # filename.REALNAME.scenario + name = scenario_path.replace(mfile + ".", "").replace( + "." + self.FILE_EXTENSION, "") + path = scenario_path + else: + name = section + path = None + + scenarios.append(Scenario(name, config.items(section), path)) + + if not scenario_paths: + self.discovered = True + self.all_scenarios.extend(scenarios) + + return scenarios + + def get_scenario(self, name): + if name is not None and os.path.isabs(name) and name.endswith(self.FILE_EXTENSION): + scenarios = self.discover_scenarios([name]) + + if scenarios: + return scenarios[0] + + if self.discovered is False: + self.discover_scenarios() + + if name is None: + return self.all_scenarios + + try: + return [scenario for scenario in self.all_scenarios if scenario.name == name][0] + except IndexError: + self.warning("Scenario: %s not found" % name) + return None + + +class GstValidateBaseTestManager(TestsManager): + scenarios_manager = ScenarioManager() + + def __init__(self): + super(GstValidateBaseTestManager, self).__init__() + self._scenarios = [] + self._encoding_formats = [] + + def add_scenarios(self, scenarios): + """ + @scenarios A list or a unic scenario name(s) to be run on the tests. + They are just the default scenarios, and then depending on + the TestsGenerator to be used you can have more fine grained + control on what to be run on each serie of tests. + """ + if isinstance(scenarios, list): + self._scenarios.extend(scenarios) + else: + self._scenarios.append(scenarios) + + self._scenarios = list(set(self._scenarios)) + + def get_scenarios(self): + return self._scenarios + + def add_encoding_formats(self, encoding_formats): + """ + :param encoding_formats: A list or one single #MediaFormatCombinations describing wanted output + formats for transcoding test. + They are just the default encoding formats, and then depending on + the TestsGenerator to be used you can have more fine grained + control on what to be run on each serie of tests. + """ + if isinstance(encoding_formats, list): + self._encoding_formats.extend(encoding_formats) + else: + self._encoding_formats.append(encoding_formats) + + self._encoding_formats = list(set(self._encoding_formats)) + + def get_encoding_formats(self): + return self._encoding_formats + + +class MediaDescriptor(Loggable): + + def __init__(self): + Loggable.__init__(self) + + def get_path(self): + raise NotImplemented + + def get_media_filepath(self): + raise NotImplemented + + def get_caps(self): + raise NotImplemented + + def get_uri(self): + raise NotImplemented + + def get_duration(self): + raise NotImplemented + + def get_protocol(self): + raise NotImplemented + + def is_seekable(self): + raise NotImplemented + + def is_image(self): + raise NotImplemented + + def get_num_tracks(self, track_type): + raise NotImplemented + + def can_play_reverse(self): + raise NotImplemented + + def is_compatible(self, scenario): + if scenario is None: + return True + + if scenario.seeks() and (not self.is_seekable() or self.is_image()): + self.debug("Do not run %s as %s does not support seeking", + scenario, self.get_uri()) + return False + + if self.is_image() and scenario.needs_clock_sync(): + self.debug("Do not run %s as %s is an image", + scenario, self.get_uri()) + return False + + if not self.can_play_reverse() and scenario.does_reverse_playback(): + return False + + if self.get_duration() and self.get_duration() / GST_SECOND < scenario.get_min_media_duration(): + self.debug( + "Do not run %s as %s is too short (%i < min media duation : %i", + scenario, self.get_uri(), + self.get_duration() / GST_SECOND, + scenario.get_min_media_duration()) + return False + + for track_type in ['audio', 'subtitle']: + if self.get_num_tracks(track_type) < scenario.get_min_tracks(track_type): + self.debug("%s -- %s | At least %s %s track needed < %s" + % (scenario, self.get_uri(), track_type, + scenario.get_min_tracks(track_type), + self.get_num_tracks(track_type))) + return False + + return True + + +class GstValidateMediaDescriptor(MediaDescriptor): + # Some extension file for discovering results + MEDIA_INFO_EXT = "media_info" + STREAM_INFO_EXT = "stream_info" + + DISCOVERER_COMMAND = "gst-validate-media-check-1.0" + if "win32" in sys.platform: + DISCOVERER_COMMAND += ".exe" + + def __init__(self, xml_path): + super(GstValidateMediaDescriptor, self).__init__() + + self._xml_path = xml_path + self.media_xml = ET.parse(xml_path).getroot() + + # Sanity checks + self.media_xml.attrib["duration"] + self.media_xml.attrib["seekable"] + + self.set_protocol(urlparse.urlparse(urlparse.urlparse(self.get_uri()).scheme).scheme) + + @staticmethod + def new_from_uri(uri, verbose=False, full=False): + media_path = utils.url2path(uri) + descriptor_path = "%s.%s" % ( + media_path, GstValidateMediaDescriptor.MEDIA_INFO_EXT) + args = GstValidateMediaDescriptor.DISCOVERER_COMMAND.split(" ") + args.append(uri) + + args.extend(["--output-file", descriptor_path]) + if full: + args.extend(["--full"]) + + if verbose: + printc("Generating media info for %s\n" + " Command: '%s'" % (media_path, ' '.join(args)), + Colors.OKBLUE) + + try: + subprocess.check_output(args, stderr=open(os.devnull)) + except subprocess.CalledProcessError as e: + if verbose: + printc("Result: Failed", Colors.FAIL) + else: + loggable.warning("GstValidateMediaDescriptor", "Exception: %s" % e) + return None + + if verbose: + printc("Result: Passed", Colors.OKGREEN) + + return GstValidateMediaDescriptor(descriptor_path) + + def get_path(self): + return self._xml_path + + def need_clock_sync(self): + return Protocols.needs_clock_sync(self.get_protocol()) + + def get_media_filepath(self): + if self.get_protocol() == Protocols.FILE: + return self._xml_path.replace("." + self.MEDIA_INFO_EXT, "") + else: + return self._xml_path.replace("." + self.STREAM_INFO_EXT, "") + + def get_caps(self): + return self.media_xml.findall("streams")[0].attrib["caps"] + + def get_tracks_caps(self): + res = [] + try: + streams = self.media_xml.findall("streams")[0].findall("stream") + except IndexError: + return res + + for stream in streams: + res.append((stream.attrib["type"], stream.attrib["caps"])) + + return res + + def get_uri(self): + return self.media_xml.attrib["uri"] + + def get_duration(self): + return long(self.media_xml.attrib["duration"]) + + def set_protocol(self, protocol): + self.media_xml.attrib["protocol"] = protocol + + def get_protocol(self): + return self.media_xml.attrib["protocol"] + + def is_seekable(self): + return self.media_xml.attrib["seekable"] + + def can_play_reverse(self): + return True + + def is_image(self): + for stream in self.media_xml.findall("streams")[0].findall("stream"): + if stream.attrib["type"] == "image": + return True + return False + + def get_num_tracks(self, track_type): + n = 0 + for stream in self.media_xml.findall("streams")[0].findall("stream"): + if stream.attrib["type"] == track_type: + n += 1 + + return n + + def get_clean_name(self): + name = os.path.basename(self.get_path()) + name = re.sub("\.stream_info|\.media_info", "", name) + + return name.replace('.', "_") + + +class MediaFormatCombination(object): + FORMATS = {"aac": "audio/mpeg,mpegversion=4", + "ac3": "audio/x-ac3", + "vorbis": "audio/x-vorbis", + "mp3": "audio/mpeg,mpegversion=1,layer=3", + "h264": "video/x-h264", + "vp8": "video/x-vp8", + "theora": "video/x-theora", + "ogg": "application/ogg", + "mkv": "video/x-matroska", + "mp4": "video/quicktime,variant=iso;", + "webm": "video/webm"} + + def __str__(self): + return "%s and %s in %s" % (self.audio, self.video, self.container) + + def __init__(self, container, audio, video): + """ + Describes a media format to be used for transcoding tests. + + :param container: A string defining the container format to be used, must bin in self.FORMATS + :param audio: A string defining the audio format to be used, must bin in self.FORMATS + :param video: A string defining the video format to be used, must bin in self.FORMATS + """ + self.container = container + self.audio = audio + self.video = video + + def get_caps(self, track_type): + try: + return self.FORMATS[self.__dict__[track_type]] + except KeyError: + return None + + def get_audio_caps(self): + return self.get_caps("audio") + + def get_video_caps(self): + return self.get_caps("video") + + def get_muxer_caps(self): + return self.get_caps("container") diff --git a/validate/launcher/config.py.in b/validate/launcher/config.py.in new file mode 100644 index 0000000..5773522 --- /dev/null +++ b/validate/launcher/config.py.in @@ -0,0 +1,21 @@ +#!/usr/bin/env python2 +# +# Copyright (c) 2015,Thibault Saunier +# +# This program 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.1 of the License, or (at your option) any later version. +# +# This program 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 program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301, USA. + +LIBDIR = '@LIBDIR@' +DATADIR = '@DATADIR@' diff --git a/validate/launcher/httpserver.py b/validate/launcher/httpserver.py new file mode 100644 index 0000000..c291ea6 --- /dev/null +++ b/validate/launcher/httpserver.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python2 +# +# Copyright (c) 2013,Thibault Saunier +# +# This program 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.1 of the License, or (at your option) any later version. +# +# This program 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 program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301, USA. + +import os +import time +import loggable +import subprocess +import sys + +logcat = "httpserver" + + +class HTTPServer(loggable.Loggable): + + """ Class to run a SimpleHttpServer in a process.""" + + def __init__(self, options): + loggable.Loggable.__init__(self) + self.options = options + self._process = None + self._logsfile = None + + def _check_is_up(self, timeout=60): + """ Check if the server is up, running a simple test based on wget. """ + start = time.time() + while True: + try: + subprocess.check_output(["wget", "127.0.0.1:%s" % + (self.options.http_server_port), + "-O", os.devnull], + stderr=self._logsfile) + return True + except subprocess.CalledProcessError: + pass + + if time.time() - start > timeout: + return False + + time.sleep(1) + + def start(self): + """ Start the server in a subprocess """ + self._logsfile = open(os.path.join(self.options.logsdir, + "httpserver.logs"), 'w+') + if self.options.http_server_dir is not None: + if self._check_is_up(timeout=2): + return True + + print "Starting Server" + try: + self.debug("Lunching http server") + cmd = "%s %s %d %s" % (sys.executable, os.path.join(os.path.dirname(__file__), + "RangeHTTPServer.py"), + self.options.http_server_port, + self.options.http_bandwith, + ) + curdir = os.path.abspath(os.curdir) + os.chdir(self.options.http_server_dir) + # cmd = "twistd -no web --path=%s -p %d" % ( + # self.options.http_server_dir, self.options.http_server_port) + self.debug( + "Lunching server: %s (logs in %s)", cmd, self._logsfile) + self._process = subprocess.Popen(cmd.split(" "), + stderr=self._logsfile, + stdout=self._logsfile) + os.chdir(curdir) + self.debug("Lunched http server") + # Dirty way to avoid eating to much CPU... + # good enough for us anyway. + time.sleep(1) + + if self._check_is_up(): + print "Started" + return True + else: + print "Failed starting server" + self._process.terminate() + self._process = None + except OSError as ex: + print "Failed starting server" + self.warning(logcat, "Could not launch server %s" % ex) + + return False + + def stop(self): + """ Stop the server subprocess if running. """ + if self._process: + self._process.terminate() + self._process = None + self.debug("Server stopped") diff --git a/validate/launcher/loggable.py b/validate/launcher/loggable.py new file mode 100644 index 0000000..d033ece --- /dev/null +++ b/validate/launcher/loggable.py @@ -0,0 +1,1237 @@ +# CC'd from 'pitivi/log/loggable.py' +# +# Copyright (c) 2009, Alessandro Decina +# +# This program 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.1 of the License, or (at your option) any later version. +# +# This program 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 program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301, USA. + +import errno +import sys +import re +import os +import fnmatch +import time +import types +import traceback +import thread + + +# environment variables controlling levels for each category +_DEBUG = "*:1" +# name of the environment variable controlling our logging +_ENV_VAR_NAME = None +# package names we should scrub filenames for +_PACKAGE_SCRUB_LIST = [] + +# dynamic dictionary of categories already seen and their level +_categories = {} + +# log handlers registered +_log_handlers = [] +_log_handlers_limited = [] + +_initialized = False +_enableCrackOutput = False + +_stdout = None +_stderr = None +_old_hup_handler = None + + +# public log levels +(ERROR, + WARN, + FIXME, + INFO, + DEBUG, + LOG) = range(1, 7) + +COLORS = {ERROR: 'RED', + WARN: 'YELLOW', + FIXME: 'MAGENTA', + INFO: 'GREEN', + DEBUG: 'BLUE', + LOG: 'CYAN'} + +_FORMATTED_LEVELS = [] +_LEVEL_NAMES = ['ERROR', 'WARN', 'FIXME', 'INFO', 'DEBUG', 'LOG'] + + +class TerminalController(object): + + """ + A class that can be used to portably generate formatted output to + a terminal. + + `TerminalController` defines a set of instance variables whose + values are initialized to the control sequence necessary to + perform a given action. These can be simply included in normal + output to the terminal: + + >>> term = TerminalController() + >>> print 'This is '+term.GREEN+'green'+term.NORMAL + + Alternatively, the `render()` method can used, which replaces + '${action}' with the string required to perform 'action': + + >>> term = TerminalController() + >>> print term.render('This is ${GREEN}green${NORMAL}') + + If the terminal doesn't support a given action, then the value of + the corresponding instance variable will be set to ''. As a + result, the above code will still work on terminals that do not + support color, except that their output will not be colored. + Also, this means that you can test whether the terminal supports a + given action by simply testing the truth value of the + corresponding instance variable: + + >>> term = TerminalController() + >>> if term.CLEAR_SCREEN: + ... print 'This terminal supports clearning the screen.' + + Finally, if the width and height of the terminal are known, then + they will be stored in the `COLS` and `LINES` attributes. + """ + # Cursor movement: + BOL = '' # : Move the cursor to the beginning of the line + UP = '' # : Move the cursor up one line + DOWN = '' # : Move the cursor down one line + LEFT = '' # : Move the cursor left one char + RIGHT = '' # : Move the cursor right one char + + # Deletion: + CLEAR_SCREEN = '' # : Clear the screen and move to home position + CLEAR_EOL = '' # : Clear to the end of the line. + CLEAR_BOL = '' # : Clear to the beginning of the line. + CLEAR_EOS = '' # : Clear to the end of the screen + + # Output modes: + BOLD = '' # : Turn on bold mode + BLINK = '' # : Turn on blink mode + DIM = '' # : Turn on half-bright mode + REVERSE = '' # : Turn on reverse-video mode + NORMAL = '' # : Turn off all modes + + # Cursor display: + HIDE_CURSOR = '' # : Make the cursor invisible + SHOW_CURSOR = '' # : Make the cursor visible + + # Terminal size: + COLS = None # : Width of the terminal (None for unknown) + LINES = None # : Height of the terminal (None for unknown) + + # Foreground colors: + BLACK = BLUE = GREEN = CYAN = RED = MAGENTA = YELLOW = WHITE = '' + + # Background colors: + BG_BLACK = BG_BLUE = BG_GREEN = BG_CYAN = '' + BG_RED = BG_MAGENTA = BG_YELLOW = BG_WHITE = '' + + _STRING_CAPABILITIES = """ + BOL=cr UP=cuu1 DOWN=cud1 LEFT=cub1 RIGHT=cuf1 + CLEAR_SCREEN=clear CLEAR_EOL=el CLEAR_BOL=el1 CLEAR_EOS=ed BOLD=bold + BLINK=blink DIM=dim REVERSE=rev UNDERLINE=smul NORMAL=sgr0 + HIDE_CURSOR=cinvis SHOW_CURSOR=cnorm""".split() + _COLORS = """BLACK BLUE GREEN CYAN RED MAGENTA YELLOW WHITE""".split() + _ANSICOLORS = "BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE".split() + + def __init__(self, term_stream=sys.stdout): + """ + Create a `TerminalController` and initialize its attributes + with appropriate values for the current terminal. + `term_stream` is the stream that will be used for terminal + output; if this stream is not a tty, then the terminal is + assumed to be a dumb terminal (i.e., have no capabilities). + """ + # Curses isn't available on all platforms + try: + import curses + except ImportError: + return + + # If the stream isn't a tty, then assume it has no capabilities. + if not term_stream.isatty(): + return + + # Check the terminal type. If we fail, then assume that the + # terminal has no capabilities. + try: + curses.setupterm() + except: + return + + # Look up numeric capabilities. + self.COLS = curses.tigetnum('cols') + self.LINES = curses.tigetnum('lines') + + # Look up string capabilities. + for capability in self._STRING_CAPABILITIES: + (attrib, cap_name) = capability.split('=') + setattr(self, attrib, self._tigetstr(cap_name) or '') + + # Colors + set_fg = self._tigetstr('setf') + if set_fg: + for i, color in zip(range(len(self._COLORS)), self._COLORS): + setattr(self, color, curses.tparm(set_fg, i) or '') + set_fg_ansi = self._tigetstr('setaf') + if set_fg_ansi: + for i, color in zip(range(len(self._ANSICOLORS)), + self._ANSICOLORS): + setattr(self, color, curses.tparm(set_fg_ansi, i) or '') + set_bg = self._tigetstr('setb') + if set_bg: + for i, color in zip(range(len(self._COLORS)), self._COLORS): + setattr(self, 'BG_' + color, curses.tparm(set_bg, i) or '') + set_bg_ansi = self._tigetstr('setab') + if set_bg_ansi: + for i, color in zip(range(len(self._ANSICOLORS)), + self._ANSICOLORS): + setattr( + self, 'BG_' + color, curses.tparm(set_bg_ansi, i) or '') + + def _tigetstr(self, cap_name): + # String capabilities can include "delays" of the form "$<2>". + # For any modern terminal, we should be able to just ignore + # these, so strip them out. + import curses + cap = curses.tigetstr(cap_name) or '' + return re.sub(r'\$<\d+>[/*]?', '', cap) + + def render(self, template): + """ + Replace each $-substitutions in the given template string with + the corresponding terminal control string (if it's defined) or + '' (if it's not). + """ + return re.sub(r'\$\$|\${\w+}', self._render_sub, template) + + def _render_sub(self, match): + s = match.group() + if s == '$$': + return s + else: + return getattr(self, s[2:-1]) + +# +# Example use case: progress bar +# + + +class ProgressBar: + + """ + A 3-line progress bar, which looks like:: + + Header + 20% [===========----------------------------------] + progress message + + The progress bar is colored, if the terminal supports color + output; and adjusts to the width of the terminal. + """ + BAR = '%3d%% ${GREEN}[${BOLD}%s%s${NORMAL}${GREEN}]${NORMAL}\n' + HEADER = '${BOLD}${CYAN}%s${NORMAL}\n\n' + + def __init__(self, term, header): + self.term = term + if not (self.term.CLEAR_EOL and self.term.UP and self.term.BOL): + raise ValueError("Terminal isn't capable enough -- you " + "should use a simpler progress dispaly.") + self.width = self.term.COLS or 75 + self.bar = term.render(self.BAR) + self.header = self.term.render(self.HEADER % header.center(self.width)) + self.cleared = 1 # : true if we haven't drawn the bar yet. + self.update(0, '') + + def update(self, percent, message): + if self.cleared: + sys.stdout.write(self.header) + self.cleared = 0 + n = int((self.width - 10) * percent) + sys.stdout.write( + self.term.BOL + self.term.UP + self.term.CLEAR_EOL + + (self.bar % (100 * percent, '=' * n, '-' * (self.width - 10 - n))) + + self.term.CLEAR_EOL + message.center(self.width)) + + def clear(self): + if not self.cleared: + sys.stdout.write(self.term.BOL + self.term.CLEAR_EOL + + self.term.UP + self.term.CLEAR_EOL + + self.term.UP + self.term.CLEAR_EOL) + self.cleared = 1 + + +def getLevelName(level): + """ + Return the name of a log level. + @param level: The level we want to know the name + @type level: int + @return: The name of the level + @rtype: str + """ + assert isinstance(level, int) and level > 0 and level < 6, \ + TypeError("Bad debug level") + return getLevelNames()[level - 1] + + +def getLevelNames(): + """ + Return a list with the level names + @return: A list with the level names + @rtype: list of str + """ + return _LEVEL_NAMES + + +def getLevelInt(levelName): + """ + Return the integer value of the levelName. + @param levelName: The string value of the level name + @type levelName: str + @return: The value of the level name we are interested in. + @rtype: int + """ + assert isinstance(levelName, str) and levelName in getLevelNames(), \ + "Bad debug level name" + return getLevelNames().index(levelName) + 1 + + +def getFormattedLevelName(level): + assert isinstance(level, int) and level > 0 and level < len(_LEVEL_NAMES) + 1, \ + TypeError("Bad debug level") + return _FORMATTED_LEVELS[level - 1] + + +def registerCategory(category): + """ + Register a given category in the debug system. + A level will be assigned to it based on previous calls to setDebug. + """ + # parse what level it is set to based on _DEBUG + # example: *:2,admin:4 + global _DEBUG + global _levels + global _categories + + level = 0 + chunks = _DEBUG.split(',') + for chunk in chunks: + if not chunk: + continue + if ':' in chunk: + spec, value = chunk.split(':') + else: + spec = '*' + value = chunk + + # our glob is unix filename style globbing, so cheat with fnmatch + # fnmatch.fnmatch didn't work for this, so don't use it + if category in fnmatch.filter((category, ), spec): + # we have a match, so set level based on string or int + if not value: + continue + try: + level = int(value) + except ValueError: # e.g. *; we default to most + level = 5 + # store it + _categories[category] = level + + +def getCategoryLevel(category): + """ + @param category: string + + Get the debug level at which this category is being logged, adding it + if it wasn't registered yet. + """ + global _categories + if category not in _categories: + registerCategory(category) + return _categories[category] + + +def setLogSettings(state): + """Update the current log settings. + This can restore an old saved log settings object returned by + getLogSettings + @param state: the settings to set + """ + + global _DEBUG + global _log_handlers + global _log_handlers_limited + + (_DEBUG, + _categories, + _log_handlers, + _log_handlers_limited) = state + + for category in _categories: + registerCategory(category) + + +def getLogSettings(): + """Fetches the current log settings. + The returned object can be sent to setLogSettings to restore the + returned settings + @returns: the current settings + """ + return (_DEBUG, + _categories, + _log_handlers, + _log_handlers_limited) + + +def _canShortcutLogging(category, level): + if _log_handlers: + # we have some loggers operating without filters, have to do + # everything + return False + else: + return level > getCategoryLevel(category) + + +def scrubFilename(filename): + ''' + Scrub the filename to a relative path for all packages in our scrub list. + ''' + global _PACKAGE_SCRUB_LIST + for package in _PACKAGE_SCRUB_LIST: + i = filename.rfind(package) + if i > -1: + return filename[i:] + + return filename + + +def getFileLine(where=-1): + """ + Return the filename and line number for the given location. + + If where is a negative integer, look for the code entry in the current + stack that is the given number of frames above this module. + If where is a function, look for the code entry of the function. + + @param where: how many frames to go back up, or function + @type where: int (negative) or function + + @return: tuple of (file, line) + @rtype: tuple of (str, int) + """ + co = None + lineno = None + name = None + + if isinstance(where, types.FunctionType): + co = where.func_code + lineno = co.co_firstlineno + name = co.co_name + elif isinstance(where, types.MethodType): + co = where.im_func.func_code + lineno = co.co_firstlineno + name = co.co_name + else: + stackFrame = sys._getframe() + while stackFrame: + co = stackFrame.f_code + if not co.co_filename.endswith('loggable.py'): + # wind up the stack according to frame + while where < -1: + stackFrame = stackFrame.f_back + where += 1 + co = stackFrame.f_code + lineno = stackFrame.f_lineno + name = co.co_name + break + stackFrame = stackFrame.f_back + + if not co: + return "", 0 + + return scrubFilename(co.co_filename), lineno, name + + +def ellipsize(o): + """ + Ellipsize the representation of the given object. + """ + r = repr(o) + if len(r) < 800: + return r + + r = r[:60] + ' ... ' + r[-15:] + return r + + +def getFormatArgs(startFormat, startArgs, endFormat, endArgs, args, kwargs): + """ + Helper function to create a format and args to use for logging. + This avoids needlessly interpolating variables. + """ + debugArgs = startArgs[:] + for a in args: + debugArgs.append(ellipsize(a)) + + for items in kwargs.items(): + debugArgs.extend(items) + debugArgs.extend(endArgs) + format = startFormat \ + + ', '.join(('%s', ) * len(args)) \ + + (kwargs and ', ' or '') \ + + ', '.join(('%s=%r', ) * len(kwargs)) \ + + endFormat + return format, debugArgs + + +def doLog(level, object, category, format, args, where=-1, filePath=None, line=None): + """ + @param where: what to log file and line number for; + -1 for one frame above log.py; -2 and down for higher up; + a function for a (future) code object + @type where: int or callable + @param filePath: file to show the message as coming from, if caller + knows best + @type filePath: str + @param line: line to show the message as coming from, if caller + knows best + @type line: int + + @return: dict of calculated variables, if they needed calculating. + currently contains file and line; this prevents us from + doing this work in the caller when it isn't needed because + of the debug level + """ + ret = {} + + if args: + message = format % args + else: + message = format + + # first all the unlimited ones + if _log_handlers: + if filePath is None and line is None: + (filePath, line, funcname) = getFileLine(where=where) + ret['filePath'] = filePath + ret['line'] = line + if funcname: + message = "\033[00m\033[32;01m%s:\033[00m %s" % (funcname, message) + for handler in _log_handlers: + try: + handler(level, object, category, file, line, message) + except TypeError, e: + raise SystemError("handler %r raised a TypeError: %s" % ( + handler, getExceptionMessage(e))) + + if level > getCategoryLevel(category): + return ret + + if _log_handlers_limited: + if filePath is None and line is None: + (filePath, line, funcname) = getFileLine(where=where) + ret['filePath'] = filePath + ret['line'] = line + if funcname: + message = "\033[00m\033[32;01m%s:\033[00m %s" % (funcname, message) + for handler in _log_handlers_limited: + # set this a second time, just in case there weren't unlimited + # loggers there before + try: + handler(level, object, category, filePath, line, message) + except TypeError: + raise SystemError("handler %r raised a TypeError" % handler) + + return ret + + +def errorObject(object, cat, format, *args): + """ + Log a fatal error message in the given category. + This will also raise a L{SystemExit}. + """ + doLog(ERROR, object, cat, format, args) + + # we do the import here because having it globally causes weird import + # errors if our gstreactor also imports .log, which brings in errors + # and pb stuff + if args: + raise SystemExit(format % args) + else: + raise SystemExit(format) + + +def warningObject(object, cat, format, *args): + """ + Log a warning message in the given category. + This is used for non-fatal problems. + """ + doLog(WARN, object, cat, format, args) + + +def fixmeObject(object, cat, format, *args): + """ + Log a fixme message in the given category. + This is used for not implemented codepaths or known issues in the code + """ + doLog(FIXME, object, cat, format, args) + + +def infoObject(object, cat, format, *args): + """ + Log an informational message in the given category. + """ + doLog(INFO, object, cat, format, args) + + +def debugObject(object, cat, format, *args): + """ + Log a debug message in the given category. + """ + doLog(DEBUG, object, cat, format, args) + + +def logObject(object, cat, format, *args): + """ + Log a log message. Used for debugging recurring events. + """ + doLog(LOG, object, cat, format, args) + + +def safeprintf(file, format, *args): + """Write to a file object, ignoring errors. + """ + try: + if args: + file.write(format % args) + else: + file.write(format) + except IOError, e: + if e.errno == errno.EPIPE: + # if our output is closed, exit; e.g. when logging over an + # ssh connection and the ssh connection is closed + os._exit(os.EX_OSERR) + # otherwise ignore it, there's nothing you can do + + +def stderrHandler(level, object, category, file, line, message): + """ + A log handler that writes to stderr. + The output will be different depending the value of "_enableCrackOutput"; + in Pitivi's case, that is True when the GST_DEBUG env var is defined. + + @type level: string + @type object: string (or None) + @type category: string + @type message: string + """ + + # Make the file path more compact for readability + file = os.path.relpath(file) + where = "(%s:%d)" % (file, line) + + # If GST_DEBUG is not set, we can assume only PITIVI_DEBUG is set, so don't + # show a bazillion of debug details that are not relevant to Pitivi. + if not _enableCrackOutput: + safeprintf(sys.stderr, '%s %-8s %-17s %-2s %s %s\n', + getFormattedLevelName(level), time.strftime("%H:%M:%S"), + category, "", message, where) + else: + o = "" + if object: + o = '"' + object + '"' + # level pid object cat time + # 5 + 1 + 7 + 1 + 32 + 1 + 17 + 1 + 15 == 80 + safeprintf( + sys.stderr, '%s [%5d] [0x%12x] %-32s %-17s %-15s %-4s %s %s\n', + getFormattedLevelName(level), os.getpid(), thread.get_ident(), + o[:32], category, time.strftime("%b %d %H:%M:%S"), "", + message, where) + sys.stderr.flush() + + +def _colored_formatter(level): + format = '%-5s' + + t = TerminalController() + return ''.join((t.BOLD, getattr(t, COLORS[level]), + format % (_LEVEL_NAMES[level - 1], ), t.NORMAL)) + + +def _formatter(level): + format = '%-5s' + return format % (_LEVEL_NAMES[level - 1], ) + + +def _preformatLevels(noColorEnvVarName): + if (noColorEnvVarName is not None + and (noColorEnvVarName not in os.environ + or not os.environ[noColorEnvVarName])): + formatter = _colored_formatter + else: + formatter = _formatter + + for level in ERROR, WARN, FIXME, INFO, DEBUG, LOG: + _FORMATTED_LEVELS.append(formatter(level)) + +# "public" useful API + +# setup functions + + +def init(envVarName, enableColorOutput=False, enableCrackOutput=True): + """ + Initialize the logging system and parse the environment variable + of the given name. + Needs to be called before starting the actual application. + """ + global _initialized + global _enableCrackOutput + _enableCrackOutput = enableCrackOutput + + if _initialized: + return + + global _ENV_VAR_NAME + _ENV_VAR_NAME = envVarName + + if enableColorOutput: + _preformatLevels(envVarName + "_NO_COLOR") + else: + _preformatLevels(None) + + if envVarName in os.environ: + # install a log handler that uses the value of the environment var + setDebug(os.environ[envVarName]) + addLimitedLogHandler(stderrHandler) + + _initialized = True + + +def setDebug(string): + """Set the DEBUG string. This controls the log output.""" + global _DEBUG + global _ENV_VAR_NAME + global _categories + + _DEBUG = string + debug('log', "%s set to %s" % (_ENV_VAR_NAME, _DEBUG)) + + # reparse all already registered category levels + for category in _categories: + registerCategory(category) + + +def getDebug(): + """ + Returns the currently active DEBUG string. + @rtype: str + """ + global _DEBUG + return _DEBUG + + +def setPackageScrubList(*packages): + """ + Set the package names to scrub from filenames. + Filenames from these paths in log messages will be scrubbed to their + relative file path instead of the full absolute path. + + @type packages: list of str + """ + global _PACKAGE_SCRUB_LIST + _PACKAGE_SCRUB_LIST = packages + + +def reset(): + """ + Resets the logging system, removing all log handlers. + """ + global _log_handlers, _log_handlers_limited, _initialized + + _log_handlers = [] + _log_handlers_limited = [] + _initialized = False + + +def addLogHandler(func): + """ + Add a custom log handler. + + @param func: a function object with prototype (level, object, category, + message) where level is either ERROR, WARN, INFO, DEBUG, or + LOG, and the rest of the arguments are strings or None. Use + getLevelName(level) to get a printable name for the log level. + @type func: a callable function + + @raises TypeError: if func is not a callable + """ + + if not callable(func): + raise TypeError("func must be callable") + + if func not in _log_handlers: + _log_handlers.append(func) + + +def addLimitedLogHandler(func): + """ + Add a custom log handler. + + @param func: a function object with prototype (level, object, category, + message) where level is either ERROR, WARN, INFO, DEBUG, or + LOG, and the rest of the arguments are strings or None. Use + getLevelName(level) to get a printable name for the log level. + @type func: a callable function + + @raises TypeError: TypeError if func is not a callable + """ + if not callable(func): + raise TypeError("func must be callable") + + if func not in _log_handlers_limited: + _log_handlers_limited.append(func) + + +def removeLogHandler(func): + """ + Remove a registered log handler. + + @param func: a function object with prototype (level, object, category, + message) where level is either ERROR, WARN, INFO, DEBUG, or + LOG, and the rest of the arguments are strings or None. Use + getLevelName(level) to get a printable name for the log level. + @type func: a callable function + + @raises ValueError: if func is not registered + """ + _log_handlers.remove(func) + + +def removeLimitedLogHandler(func): + """ + Remove a registered limited log handler. + + @param func: a function object with prototype (level, object, category, + message) where level is either ERROR, WARN, INFO, DEBUG, or + LOG, and the rest of the arguments are strings or None. Use + getLevelName(level) to get a printable name for the log level. + @type func: a callable function + + @raises ValueError: if func is not registered + """ + _log_handlers_limited.remove(func) + +# public log functions + + +def error(cat, format, *args): + errorObject(None, cat, format, *args) + + +def warning(cat, format, *args): + warningObject(None, cat, format, *args) + + +def fixme(cat, format, *args): + fixmeObject(None, cat, format, *args) + + +def info(cat, format, *args): + infoObject(None, cat, format, *args) + + +def debug(cat, format, *args): + debugObject(None, cat, format, *args) + + +def log(cat, format, *args): + logObject(None, cat, format, *args) + +# public utility functions + + +def getExceptionMessage(exception, frame=-1, filename=None): + """ + Return a short message based on an exception, useful for debugging. + Tries to find where the exception was triggered. + """ + stack = traceback.extract_tb(sys.exc_info()[2]) + if filename: + stack = [f for f in stack if f[0].find(filename) > -1] + # import code; code.interact(local=locals()) + (filename, line, func, text) = stack[frame] + filename = scrubFilename(filename) + exc = exception.__class__.__name__ + msg = "" + # a shortcut to extract a useful message out of most exceptions + # for now + if str(exception): + msg = ": %s" % str(exception) + return "exception %(exc)s at %(filename)s:%(line)s: %(func)s()%(msg)s" \ + % locals() + + +def reopenOutputFiles(): + """ + Reopens the stdout and stderr output files, as set by + L{outputToFiles}. + """ + if not _stdout and not _stderr: + debug('log', 'told to reopen log files, but log files not set') + return + + def reopen(name, fileno, *args): + oldmask = os.umask(0026) + try: + f = open(name, 'a+', *args) + finally: + os.umask(oldmask) + + os.dup2(f.fileno(), fileno) + + if _stdout: + reopen(_stdout, sys.stdout.fileno()) + + if _stderr: + reopen(_stderr, sys.stderr.fileno(), 0) + debug('log', 'opened log %r', _stderr) + + +def outputToFiles(stdout=None, stderr=None): + """ + Redirect stdout and stderr to named files. + + Records the file names so that a future call to reopenOutputFiles() + can open the same files. Installs a SIGHUP handler that will reopen + the output files. + + Note that stderr is opened unbuffered, so if it shares a file with + stdout then interleaved output may not appear in the order that you + expect. + """ + global _stdout, _stderr, _old_hup_handler + _stdout, _stderr = stdout, stderr + reopenOutputFiles() + + def sighup(signum, frame): + info('log', "Received SIGHUP, reopening logs") + reopenOutputFiles() + if _old_hup_handler: + info('log', "Calling old SIGHUP hander") + _old_hup_handler(signum, frame) + + debug('log', 'installing SIGHUP handler') + import signal + handler = signal.signal(signal.SIGHUP, sighup) + if handler == signal.SIG_DFL or handler == signal.SIG_IGN: + _old_hup_handler = None + else: + _old_hup_handler = handler + + +# base class for loggable objects + + +class BaseLoggable(object): + + """ + Base class for objects that want to be able to log messages with + different level of severity. The levels are, in order from least + to most: log, debug, info, warning, error. + + @cvar logCategory: Implementors can provide a category to log their + messages under. + """ + + def writeMarker(self, marker, level): + """ + Sets a marker that written to the logs. Setting this + marker to multiple elements at a time helps debugging. + @param marker: A string write to the log. + @type marker: str + @param level: The log level. It can be log.WARN, log.INFO, + log.DEBUG, log.ERROR or log.LOG. + @type level: int + """ + logHandlers = {WARN: self.warning, + INFO: self.info, + DEBUG: self.debug, + ERROR: self.error, + LOG: self.log} + logHandler = logHandlers.get(level) + if logHandler: + logHandler('%s', marker) + + def error(self, *args): + """Log an error. By default this will also raise an exception.""" + if _canShortcutLogging(self.logCategory, ERROR): + return + errorObject(self.logObjectName(), + self.logCategory, *self.logFunction(*args)) + + def warning(self, *args): + """Log a warning. Used for non-fatal problems.""" + if _canShortcutLogging(self.logCategory, WARN): + return + warningObject( + self.logObjectName(), self.logCategory, *self.logFunction(*args)) + + def fixme(self, *args): + """Log a fixme. Used for FIXMEs .""" + if _canShortcutLogging(self.logCategory, FIXME): + return + fixmeObject(self.logObjectName(), + self.logCategory, *self.logFunction(*args)) + + def info(self, *args): + """Log an informational message. Used for normal operation.""" + if _canShortcutLogging(self.logCategory, INFO): + return + infoObject(self.logObjectName(), + self.logCategory, *self.logFunction(*args)) + + def debug(self, *args): + """Log a debug message. Used for debugging.""" + if _canShortcutLogging(self.logCategory, DEBUG): + return + debugObject(self.logObjectName(), + self.logCategory, *self.logFunction(*args)) + + def log(self, *args): + """Log a log message. Used for debugging recurring events.""" + if _canShortcutLogging(self.logCategory, LOG): + return + logObject(self.logObjectName(), + self.logCategory, *self.logFunction(*args)) + + def doLog(self, level, where, format, *args, **kwargs): + """ + Log a message at the given level, with the possibility of going + higher up in the stack. + + @param level: log level + @type level: int + @param where: how many frames to go back from the last log frame; + or a function (to log for a future call) + @type where: int (negative), or function + + @param kwargs: a dict of pre-calculated values from a previous + doLog call + + @return: a dict of calculated variables, to be reused in a + call to doLog that should show the same location + @rtype: dict + """ + if _canShortcutLogging(self.logCategory, level): + return {} + args = self.logFunction(*args) + return doLog(level, self.logObjectName(), self.logCategory, + format, args, where=where, **kwargs) + + def warningFailure(self, failure, swallow=True): + """ + Log a warning about a Twisted Failure. Useful as an errback handler: + d.addErrback(self.warningFailure) + + @param swallow: whether to swallow the failure or not + @type swallow: bool + """ + if _canShortcutLogging(self.logCategory, WARN): + if swallow: + return + return failure + warningObject(self.logObjectName(), self.logCategory, + *self.logFunction(getFailureMessage(failure))) + if not swallow: + return failure + + def logFunction(self, *args): + """Overridable log function. Default just returns passed message.""" + return args + + def logObjectName(self): + """Overridable object name function.""" + # cheat pychecker + for name in ['logName', 'name']: + if hasattr(self, name): + return getattr(self, name) + + return None + + def handleException(self, exc): + self.warning(getExceptionMessage(exc)) + +# Twisted helper stuff + +# private stuff +_initializedTwisted = False + +# make a singleton +__theTwistedLogObserver = None + + +def _getTheTwistedLogObserver(): + # used internally and in test + global __theTwistedLogObserver + + if not __theTwistedLogObserver: + __theTwistedLogObserver = TwistedLogObserver() + + return __theTwistedLogObserver + + +# public helper methods + + +def getFailureMessage(failure): + """ + Return a short message based on L{twisted.python.failure.Failure}. + Tries to find where the exception was triggered. + """ + exc = str(failure.type) + msg = failure.getErrorMessage() + if len(failure.frames) == 0: + return "failure %(exc)s: %(msg)s" % locals() + + (func, filename, line, some, other) = failure.frames[-1] + filename = scrubFilename(filename) + return "failure %(exc)s at %(filename)s:%(line)s: %(func)s(): %(msg)s" % locals() + + +def warningFailure(failure, swallow=True): + """ + Log a warning about a Failure. Useful as an errback handler: + d.addErrback(warningFailure) + + @param swallow: whether to swallow the failure or not + @type swallow: bool + """ + warning('', getFailureMessage(failure)) + if not swallow: + return failure + + +def logTwisted(): + """ + Integrate twisted's logger with our logger. + + This is done in a separate method because calling this imports and sets + up a reactor. Since we want basic logging working before choosing a + reactor, we need to separate these. + """ + global _initializedTwisted + + if _initializedTwisted: + return + + debug('log', 'Integrating twisted logger') + + # integrate twisted's logging with us + from twisted.python import log as tlog + + # this call imports the reactor + # that is why we do this in a separate method + from twisted.spread import pb + + # we don't want logs for pb.Error types since they + # are specifically raised to be handled on the other side + observer = _getTheTwistedLogObserver() + observer.ignoreErrors([pb.Error, ]) + tlog.startLoggingWithObserver(observer.emit, False) + + _initializedTwisted = True + + +# we need an object as the observer because startLoggingWithObserver +# expects a bound method + + +class TwistedLogObserver(BaseLoggable): + + """ + Twisted log observer that integrates with our logging. + """ + logCategory = "logobserver" + + def __init__(self): + self._ignoreErrors = [] # Failure types + + def emit(self, eventDict): + method = log # by default, lowest level + edm = eventDict['message'] + if not edm: + if eventDict['isError'] and 'failure' in eventDict: + f = eventDict['failure'] + for failureType in self._ignoreErrors: + r = f.check(failureType) + if r: + self.debug("Failure of type %r, ignoring", failureType) + return + + self.log("Failure %r" % f) + + method = debug # tracebacks from errors at debug level + msg = "A twisted traceback occurred." + if getCategoryLevel("twisted") < WARN: + msg += " Run with debug level >= 2 to see the traceback." + # and an additional warning + warning('twisted', msg) + text = f.getTraceback() + safeprintf(sys.stderr, "\nTwisted traceback:\n") + safeprintf(sys.stderr, text + '\n') + elif 'format' in eventDict: + text = eventDict['format'] % eventDict + else: + # we don't know how to log this + return + else: + text = ' '.join(map(str, edm)) + + fmtDict = {'system': eventDict['system'], + 'text': text.replace("\n", "\n\t")} + msgStr = " [%(system)s] %(text)s\n" % fmtDict + # because msgstr can contain %, as in a backtrace, make sure we + # don't try to splice it + method('twisted', msgStr) + + def ignoreErrors(self, *types): + for failureType in types: + self._ignoreErrors.append(failureType) + + def clearIgnores(self): + self._ignoreErrors = [] + + +class Loggable(BaseLoggable): + + def __init__(self, logCategory=None): + if logCategory: + self.logCategory = logCategory + elif not hasattr(self, 'logCategory'): + self.logCategory = self.__class__.__name__.lower() + + def logObjectName(self): + res = BaseLoggable.logObjectName(self) + if not res: + return "<%s at 0x%x>" % (self.__class__.__name__, id(self)) + return res + + def error(self, format, *args): + if _canShortcutLogging(self.logCategory, ERROR): + return + doLog(ERROR, self.logObjectName(), self.logCategory, + format, self.logFunction(*args), where=-2) diff --git a/validate/launcher/main.py b/validate/launcher/main.py new file mode 100644 index 0000000..0ac2f3c --- /dev/null +++ b/validate/launcher/main.py @@ -0,0 +1,529 @@ +#!/usr/bin/env python2 +# +# Copyright (c) 2014,Thibault Saunier +# +# This program 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.1 of the License, or (at your option) any later version. +# +# This program 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 program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301, USA. +import os +import sys +import utils +import urlparse +import loggable +import argparse +import tempfile +import reporters +import subprocess + + +from loggable import Loggable +from httpserver import HTTPServer +from vfb_server import get_virual_frame_buffer_server +from baseclasses import _TestsLauncher, ScenarioManager +from utils import printc, path2url, DEFAULT_MAIN_DIR, launch_command, Colors, Protocols, which + + +LESS = "less" +HELP = ''' + +=============================================================================== + gst-validate-launcher +=============================================================================== + +1. Introduction +---------------- + +gst-validate-launcher is a test launcher tool. It has been designed to +launch the various tools included in GstValidate, running tests on real +media files. This means that with gst-validate-launcher, you can launch +many tests automatically in one simple command. It then permits to +aggregate results and print them in a human readable way on stdout +and serializing them in the following implemented formats: + + * %s + +We support all the tools provided in GstValidate in the launcher, but +we also support ges-launch when the GStreamer Editing Services have +been compiled against GstValidate. + +2. Default test suite +--------------------- + +A default suite of tests is provided and is available at: http://cgit.freedesktop.org/gstreamer/gst-integration-testsuites/ +You can run it pretty simply doing: + +. $gst-validate-launch --sync + +That will download Gstreamer upstream default assets into the +default folder (%s) and run all currently +activated tests. Note that we use git-annex https://git-annex.branchable.com/ so +you will need that tool to get started. + +3. Implement your own tests +--------------------------- + +To implement new tests, you will just need to set the media path using the +--medias-paths argument. If you want to run all avalaible scenarios on all the +file present in that folder, you should run the first time: + +. $gst-validate-launch --medias-paths /path/to/media/files --generate-media-info + +That will generate the .media_info files that contains information about the media +files present in that folder. Those media_info files are simple XML file describing +the topology of the media files. You need not reuse --generate-media-info from +next time. The generated media files will be used as a reference for following +runs. You might want to check that they contain the right information yourself +the first time. + +Those .media_info are the files that are used by gst-validate-launcher to know +what media files can be used for the different scenarios. For example if a +file is not seekable, seeking scenarios will not be run on it etc... + +3.1 Scenarios specific to a media file/stream: +---------------------------------------------- + +It is possible that some scenarios are very specific to one media file. In that case, +the .scenario file should be present in the same folder as the .media_info file and +be called similarly. For example for a file called /some/media/file.mp4, the media_info +file will be called /some/media/file.media_info and a scenario that will seek to a position that +is known to fail would be called: /some/media/file.mp4.seek_to_failing_pos.scenario and +gst-validate-launcher will run that scenario only on that media file. + +3.2 Test media accessible through other protocols: +-------------------------------------------------- + +Currently gst-validate-launcher supports the following protocols: + + * %s + +It does not mean you can not test other protocols but it means that it has not been +properly tested. + +To test medias that use those protocols, you should simply make sure that there +is a media descriptor file with .stream_info as an extension in your --media-paths. +You can generate such a file doing: + +. $gst-validate-media-check-1.0 http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8 --output-file /somewhere/in/you/media/path/bipbop.stream_info + +Once this is done, gst-validate-launcher will run the scenarios on those media files the +same way as if they were local files. + + +4. Debug gst-validate-launcher execution +---------------------------------------- + +You can activate debug logs setting the environment variable GST_VALIDATE_LAUNCHER_DEBUG. +It uses the same syntax as PITIVI_DEBUG (more information at: +http://wiki.pitivi.org/wiki/Bug_reporting#Debug_logs). +''' % ("\n * ".join([reporter.name for reporter in + utils.get_subclasses(reporters.Reporter, reporters.__dict__)] + ), + DEFAULT_MAIN_DIR, + "\n * ".join([getattr(Protocols, att) for att in + dir(Protocols) if isinstance(getattr(Protocols, att), str) + and not att.startswith("_")])) + +QA_ASSETS = "gst-integration-testsuites" +MEDIAS_FOLDER = "medias" +DEFAULT_GST_QA_ASSETS_REPO = "git://anongit.freedesktop.org/gstreamer/gst-integration-testsuites" +OLD_DEFAULT_GST_QA_ASSETS_REPO = "https://gitlab.com/thiblahute/gst-integration-testsuites.git" +DEFAULT_TESTSUITES_DIR = os.path.join(DEFAULT_MAIN_DIR, QA_ASSETS, "testsuites") + + +def download_assets(options): + try: + launch_command("%s %s %s" % (options.get_assets_command, + options.remote_assets_url, + options.clone_dir), + fails=True) + except subprocess.CalledProcessError as e: + if "git" in options.get_assets_command: + m = "\n\nMAKE SURE YOU HAVE git INSTALLED!" + else: + m = "" + + printc("Could not download assets\n\nError: %s%s" % (e, m), + Colors.FAIL, True) + + return False + + return True + + +class PrintUsage(argparse.Action): + + def __init__(self, option_strings, dest=argparse.SUPPRESS, default=argparse.SUPPRESS, help=None): + super( + PrintUsage, self).__init__(option_strings=option_strings, dest=dest, + default=default, nargs=0, help=help) + + def __call__(self, parser, namespace, values, option_string=None): + print(HELP) + parser.exit() + + +class LauncherConfig(Loggable): + def __init__(self): + self.testsuites = [] + self.debug = False + self.forever = False + self.fatal_error = False + self.wanted_tests = [] + self.blacklisted_tests = [] + self.list_tests = False + self.mute = False + self.no_color = False + self.generate_info = False + self.update_media_info = False + self.generate_info_full = False + self.long_limit = utils.LONG_TEST + self.config = None + self.valgrind = False + self.no_display = False + self.xunit_file = None + self.main_dir = utils.DEFAULT_MAIN_DIR + self.output_dir = None + self.logsdir = None + self.redirect_logs = False + self.num_jobs = 1 + self.dest = None + self._using_default_paths = False + self.paths = [] + self.testsuites_dir = DEFAULT_TESTSUITES_DIR + + self.clone_dir = None + + self.http_server_port = 8079 + self.http_bandwith = 1024 * 1024 + self.http_server_dir = None + self.httponly = False + self.get_assets_command = "git clone" + self.remote_assets_url = DEFAULT_GST_QA_ASSETS_REPO + self.sync = False + self.sync_all = False + + def cleanup(self): + """ + Cleanup the options looking after user options have been parsed + """ + + # Get absolute path for main_dir and base everything on that + self.main_dir = os.path.abspath(self.main_dir) + + # default for output_dir is MAINDIR + if not self.output_dir: + self.output_dir = self.main_dir + else: + self.output_dir = os.path.abspath(self.output_dir) + + # other output directories + if self.logsdir in ['stdout', 'stderr']: + # Allow -l stdout/stderr to work like -rl stdout/stderr + self.redirect_logs = self.logsdir + self.logsdir = None + if self.logsdir is None: + self.logsdir = os.path.join(self.output_dir, "logs") + if self.xunit_file is None: + self.xunit_file = os.path.join(self.logsdir, "xunit.xml") + if self.dest is None: + self.dest = os.path.join(self.output_dir, "rendered") + + if not os.path.exists(self.dest): + os.makedirs(self.dest) + if not os.path.exists(self.logsdir): + os.makedirs(self.logsdir) + + if self.redirect_logs not in ['stdout', 'stderr', False]: + printc("Log redirection (%s) must be either 'stdout' or 'stderr'." + % self.redirect_logs, Colors.FAIL, True) + return False + + if urlparse.urlparse(self.dest).scheme == "": + self.dest = path2url(self.dest) + + if self.no_color: + utils.desactivate_colors() + if self.clone_dir is None: + self.clone_dir = os.path.join(self.main_dir, QA_ASSETS) + + if not isinstance(self.paths, list): + self.paths = [self.paths] + + if self.generate_info_full is True: + self.generate_info = True + + if self.sync_all is True: + self.sync = True + + if not self.sync and not os.path.exists(self.clone_dir) and \ + self.clone_dir == os.path.join(self.clone_dir, MEDIAS_FOLDER): + printc("Media path (%s) does not exists. Forgot to run --sync ?" + % self.clone_dir, Colors.FAIL, True) + return False + + if (self.main_dir != DEFAULT_MAIN_DIR or + self.clone_dir != QA_ASSETS) and \ + self.testsuites_dir == DEFAULT_TESTSUITES_DIR: + self.testsuites_dir = os.path.join(self.main_dir, self.clone_dir, + "testsuites") + if self.valgrind: + try: + subprocess.check_output("valgrind --help", shell=True) + except subprocess.CalledProcessError: + printc("Want to use valgrind, but not avalaible on the system", + Colors.FAIL) + return False + + return True + + def set_http_server_dir(self, path): + if self.http_server_dir is not None: + printc("Server directory already set to %s" % self.http_server_dir) + return + + self.http_server_dir = path + + def add_paths(self, paths): + if not isinstance(paths, list): + paths = [paths] + + if self._using_default_paths: + self.paths = paths + self._using_default_paths = False + else: + + for path in paths: + if path not in self.paths: + self.paths.append(path) + + +def main(libsdir): + if "--help" in sys.argv: + _help_message = HELP + else: + _help_message = "Use --help for the full help" + + parser = argparse.ArgumentParser( + formatter_class=argparse.RawTextHelpFormatter, + prog='gst-validate-launcher', description=_help_message) + + parser.add_argument('testsuites', metavar='N', nargs='*', + help="""Lets you specify a file where the testsuite to execute is defined. + +In the module if you want to work with a specific test manager(s) (for example, +'ges' or 'validate'), you should define the TEST_MANAGER variable in the +testsuite file (it can be a list of test manager names) + +In this file you should implement a setup_tests function. That function takes +a TestManager and the GstValidateLauncher option as parameters and return True +if it succeeded loading the tests, False otherwise. +You will be able to configure the TestManager with its various methods. This +function will be called with each TestManager usable, for example you will be +passed the 'validate' TestManager in case the GstValidateManager launcher is +avalaible. You should configure it using: + + * test_manager.add_scenarios: which allows you to register a list of scenario names to be run + * test_manager.set_default_blacklist: Lets you set a list of tuple of the form: + (@regex_defining_blacklister_test_names, @reason_for_the_blacklisting) + * test_manager.add_generators: which allows you to register a list of #GstValidateTestsGenerator + to be used to generate tests + * test_manager.add_encoding_formats:: which allows you to register a list #MediaFormatCombination to be used for transcoding tests + +You can also set default values with: + * test_manager.register_defaults: Sets default values for all parametters + * test_manager.register_default_test_generators: Sets default values for the TestsGenerators to be used + * test_manager.register_default_scenarios: Sets default values for the scenarios to be executed + * test_manager.register_default_encoding_formats: Sets default values for the encoding formats to be tested + +Note that all testsuite should be inside python modules, so the directory should contain a __init__.py file +""", + default=["validate"]) + parser.add_argument("-d", "--debug", dest="debug", + action="store_true", + help="Let user debug the process on timeout") + parser.add_argument("-f", "--forever", dest="forever", + action="store_true", + help="Keep running tests until one fails") + parser.add_argument("-F", "--fatal-error", dest="fatal_error", + action="store_true", + help="Stop on first fail") + parser.add_argument("-t", "--wanted-tests", dest="wanted_tests", + action="append", + help="Define the tests to execute, it can be a regex." + " If it contains defaults_only, only default scenarios" + " will be executed") + parser.add_argument("-b", "--blacklisted-tests", dest="blacklisted_tests", + action="append", + help="Define the tests not to execute, it can be a regex.") + parser.add_argument("-L", "--list-tests", + dest="list_tests", + action="store_true", + help="List tests and exit") + parser.add_argument("-m", "--mute", dest="mute", + action="store_true", + help="Mute playback output, which means that we use " + "a fakesink") + parser.add_argument("-n", "--no-color", dest="no_color", + action="store_true", + help="Set it to output no colored text in the terminal") + parser.add_argument("-g", "--generate-media-info", dest="generate_info", + action="store_true", + help="Set it in order to generate the missing .media_infos files") + parser.add_argument("--update-media-info", dest="update_media_info", + action="store_true", + help="Set it in order to update exising .media_infos files") + parser.add_argument( + "-G", "--generate-media-info-with-frame-detection", dest="generate_info_full", + action="store_true", + help="Set it in order to generate the missing .media_infos files. " + "It implies --generate-media-info but enabling frame detection") + parser.add_argument("-lt", "--long-test-limit", dest="long_limit", + action='store', + help="Defines the limite from which a test is concidered as long (in seconds)" + " not that 0 will enable all tests", type=int), + parser.add_argument("-c", "--config", dest="config", + help="This is DEPRECATED, prefer using the testsuite format" + " to configure testsuites") + parser.add_argument("-vg", "--valgrind", dest="valgrind", + action="store_true", + help="Run the tests inside Valgrind") + parser.add_argument("-nd", "--no-display", dest="no_display", + action="store_true", + help="Run the tests without outputing graphics" + " on any display. It tries to run all graphical operation" + " in a virtual framebuffer." + " Note that it is currently implemented only" + " for the X server thanks to Xvfb (which is requeried in that case)") + dir_group = parser.add_argument_group( + "Directories and files to be used by the launcher") + parser.add_argument('--xunit-file', action='store', + dest='xunit_file', metavar="FILE", + help=("Path to xml file to store the xunit report in. " + "Default is LOGSDIR/xunit.xml")) + dir_group.add_argument("-M", "--main-dir", dest="main_dir", + help="Main directory where to put files. Default is %s" % DEFAULT_MAIN_DIR) + dir_group.add_argument("--testsuites-dir", dest="testsuites_dir", + help="Directory where to look for testsuites. Default is %s" + " Note that GstValidate expect testsuite file to have .testsuite" + " as an extension in this folder." % DEFAULT_TESTSUITES_DIR) + dir_group.add_argument("-o", "--output-dir", dest="output_dir", + help="Directory where to store logs and rendered files. Default is MAIN_DIR") + dir_group.add_argument("-l", "--logs-dir", dest="logsdir", + help="Directory where to store logs, default is OUTPUT_DIR/logs.") + dir_group.add_argument("-R", "--render-path", dest="dest", + help="Set the path to which projects should be rendered, default is OUTPUT_DIR/rendered") + dir_group.add_argument("-p", "--medias-paths", dest="paths", action="append", + help="Paths in which to look for media files") + dir_group.add_argument("-a", "--clone-dir", dest="clone_dir", + help="Paths where to clone the testuite to run " + " default is MAIN_DIR/gst-integration-testsuites") + dir_group.add_argument("-rl", "--redirect-logs", dest="redirect_logs", + help="Redirect logs to 'stdout' or 'sdterr'.") + dir_group.add_argument("-j", "--jobs", dest="num_jobs", + help="Number of tests to execute simultaneously", + type=int) + + http_server_group = parser.add_argument_group( + "Handle the HTTP server to be created") + http_server_group.add_argument( + "--http-server-port", dest="http_server_port", + help="Port on which to run the http server on localhost") + http_server_group.add_argument( + "--http-bandwith-limitation", dest="http_bandwith", + help="The artificial bandwith limitation to introduce to the local server (in Bytes/sec) (default: 1 MBps)") + http_server_group.add_argument( + "-s", "--folder-for-http-server", dest="http_server_dir", + help="Folder in which to create an http server on localhost. Default is PATHS") + http_server_group.add_argument("--http-only", dest="httponly", + action='store_true', + help="Start the http server and quit") + + assets_group = parser.add_argument_group("Handle remote assets") + assets_group.add_argument( + "--get-assets-command", dest="get_assets_command", + help="Command to get assets") + assets_group.add_argument("--remote-assets-url", dest="remote_assets_url", + help="Url to the remote assets (default:%s)" % DEFAULT_GST_QA_ASSETS_REPO) + assets_group.add_argument("-S", "--sync", dest="sync", action="store_true", + help="Synchronize asset repository") + assets_group.add_argument("--sync-all", dest="sync_all", action="store_true", + help="Synchronize asset repository," + " including big media files") + assets_group.add_argument("--usage", action=PrintUsage, + help="Print usage documentation") + + loggable.init("GST_VALIDATE_LAUNCHER_DEBUG", True, False) + + tests_launcher = _TestsLauncher(libsdir) + tests_launcher.add_options(parser) + + if _help_message == HELP and which(LESS): + tmpf = tempfile.NamedTemporaryFile() + + parser.print_help(file=tmpf) + exit(os.system("%s %s" % (LESS, tmpf.name))) + + options = LauncherConfig() + parser.parse_args(namespace=options) + if not options.cleanup(): + exit(1) + + if options.remote_assets_url and options.sync and not os.path.exists(options.clone_dir): + if not download_assets(options): + exit(1) + + tests_launcher.set_settings(options, []) + + # Ensure that the scenario manager singleton is ready to be used + ScenarioManager().config = options + tests_launcher.list_tests() + + if options.list_tests: + l = tests_launcher.tests + for test in l: + printc(test) + + printc("\nNumber of tests: %d" % len(l), Colors.OKGREEN) + return 0 + + httpsrv = HTTPServer(options) + if tests_launcher.needs_http_server() or options.httponly is True: + httpsrv.start() + + vfb_server = get_virual_frame_buffer_server(options) + if options.no_display: + res = vfb_server.start() + if res[0] is False: + printc("Could not start virtual frame server: %s" % res[1], + Colors.FAIL) + exit(1) + os.environ["DISPLAY"] = vfb_server.display_id + + if options.httponly is True: + print "Running HTTP server only" + return + + e = None + try: + tests_launcher.run_tests() + except Exception as e: + pass + finally: + tests_launcher.final_report() + httpsrv.stop() + vfb_server.stop() + if e is not None: + raise + + return 0 diff --git a/validate/launcher/reporters.py b/validate/launcher/reporters.py new file mode 100644 index 0000000..427db32 --- /dev/null +++ b/validate/launcher/reporters.py @@ -0,0 +1,218 @@ +#!/usr/bin/env python2 +# +# Copyright (c) 2013,Thibault Saunier +# +# This program 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.1 of the License, or (at your option) any later version. +# +# This program 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 program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301, USA. + +""" Test Reporters implementation. """ + +import os +import re +import sys +import time +import codecs +import datetime +from loggable import Loggable +from xml.sax import saxutils +from utils import mkdir, Result, printc, Colors + +UNICODE_STRINGS = (type(unicode()) == type(str())) + + +class UnknownResult(Exception): + pass + + +CONTROL_CHARACTERS = re.compile(r"[\000-\010\013\014\016-\037]") + + +def xml_safe(value): + """Replaces invalid XML characters with '?'.""" + return CONTROL_CHARACTERS.sub('?', value) + + +def escape_cdata(cdata): + """Escape a string for an XML CDATA section.""" + return xml_safe(cdata).replace(']]>', ']]>]]>" + + return captured + + def _quoteattr(self, attr): + """Escape an XML attribute. Value can be unicode.""" + attr = xml_safe(attr) + if isinstance(attr, unicode) and not UNICODE_STRINGS: + attr = attr.encode(self.encoding) + return saxutils.quoteattr(attr) + + def report(self): + """Writes an Xunit-formatted XML file + + The file includes a report of test errors and failures. + + """ + self.debug("Writing XML file to: %s", self.options.xunit_file) + self.xml_file = codecs.open(self.options.xunit_file, 'w', + self.encoding, 'replace') + self.stats['encoding'] = self.encoding + self.stats['total'] = (self.stats['timeout'] + self.stats['failures'] + + self.stats['passed'] + self.stats['skipped']) + self.xml_file.write(u'' + u'' % self.stats) + self.xml_file.write(u''.join([self._forceUnicode(e) + for e in self.errorlist])) + self.xml_file.write(u'') + self.xml_file.close() + + def set_failed(self, test): + """Add failure output to Xunit report. + """ + self.stats['failures'] += 1 + self.errorlist.append( + '' + '' + '%(systemout)s' % + {'cls': self._quoteattr(test.get_classname()), + 'name': self._quoteattr(test.get_name()), + 'taken': test.time_taken, + 'errtype': self._quoteattr(test.result), + 'message': self._quoteattr(test.message), + 'systemout': self._get_captured(test), + }) + + def set_passed(self, test): + """Add success output to Xunit report. + """ + self.stats['passed'] += 1 + self.errorlist.append( + '%(systemout)s' % + {'cls': self._quoteattr(test.get_classname()), + 'name': self._quoteattr(test.get_name()), + 'taken': test.time_taken, + 'systemout': self._get_captured(test), + }) + + def _forceUnicode(self, s): + if not UNICODE_STRINGS: + if isinstance(s, str): + s = s.decode(self.encoding, 'replace') + return s diff --git a/validate/launcher/utils.py b/validate/launcher/utils.py new file mode 100644 index 0000000..6d43847 --- /dev/null +++ b/validate/launcher/utils.py @@ -0,0 +1,249 @@ +#!/usr/bin/env python2 +# +# Copyright (c) 2013,Thibault Saunier +# +# This program 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.1 of the License, or (at your option) any later version. +# +# This program 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 program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301, USA. +""" Some utilies. """ + +import config +import os +import re +import sys +import urllib +import urlparse +import subprocess + +from operator import itemgetter + + +GST_SECOND = long(1000000000) +DEFAULT_TIMEOUT = 30 +DEFAULT_MAIN_DIR = os.path.join(os.path.expanduser("~"), "gst-validate") +DEFAULT_GST_QA_ASSETS = os.path.join(DEFAULT_MAIN_DIR, "gst-integration-testsuites") +DISCOVERER_COMMAND = "gst-discoverer-1.0" +# Use to set the duration from which a test is concidered as being 'long' +LONG_TEST = 40 + + +class Result(object): + NOT_RUN = "Not run" + FAILED = "Failed" + TIMEOUT = "Timeout" + PASSED = "Passed" + KNOWN_ERROR = "Known error" + + +class Protocols(object): + HTTP = "http" + FILE = "file" + HLS = "hls" + DASH = "dash" + + @staticmethod + def needs_clock_sync(protocol): + if protocol == Protocols.HLS: + return True + + return False + + +class Colors(object): + HEADER = '\033[95m' + OKBLUE = '\033[94m' + OKGREEN = '\033[92m' + WARNING = '\033[93m' + FAIL = '\033[91m' + ENDC = '\033[0m' + + +def desactivate_colors(): + Colors.HEADER = '' + Colors.OKBLUE = '' + Colors.OKGREEN = '' + Colors.WARNING = '' + Colors.FAIL = '' + Colors.ENDC = '' + + +def mkdir(directory): + try: + os.makedirs(directory) + except os.error: + pass + + +def which(name): + result = [] + exts = filter(None, os.environ.get('PATHEXT', '').split(os.pathsep)) + path = os.environ.get('PATH', None) + if path is None: + return [] + for p in os.environ.get('PATH', '').split(os.pathsep): + p = os.path.join(p, name) + if os.access(p, os.X_OK): + result.append(p) + for e in exts: + pext = p + e + if os.access(pext, os.X_OK): + result.append(pext) + return result + + +def get_color_for_result(result): + if result is Result.FAILED: + color = Colors.FAIL + elif result is Result.TIMEOUT: + color = Colors.WARNING + elif result is Result.PASSED: + color = Colors.OKGREEN + else: + color = Colors.OKBLUE + + return color + + +def printc(message, color="", title=False): + if title: + length = 0 + for l in message.split("\n"): + if len(l) > length: + length = len(l) + if length == 0: + length = len(message) + message = length * '=' + "\n" + str(message) + "\n" + length * '=' + + if hasattr(message, "result") and color == '': + color = get_color_for_result(message.result) + + sys.stdout.write(color + str(message) + Colors.ENDC + "\n") + sys.stdout.flush() + + +def launch_command(command, color=None, fails=False): + printc(command, Colors.OKGREEN, True) + res = os.system(command) + if res != 0 and fails is True: + raise subprocess.CalledProcessError(res, "%s failed" % command) + + +def path2url(path): + return urlparse.urljoin('file:', urllib.pathname2url(path)) + + +def url2path(url): + path = urlparse.urlparse(url).path + if "win32" in sys.platform: + if path[0] == '/': + return path[1:] # We need to remove the first '/' on windows + return path + + +def isuri(string): + url = urlparse.urlparse(string) + if url.scheme != "" and url.scheme != "": + return True + + return False + + +def touch(fname, times=None): + with open(fname, 'a'): + os.utime(fname, times) + + +def get_subclasses(klass, env): + subclasses = [] + for symb in env.iteritems(): + try: + if issubclass(symb[1], klass) and not symb[1] is klass: + subclasses.append(symb[1]) + except TypeError: + pass + + return subclasses + + +def TIME_ARGS(time): + return "%u:%02u:%02u.%09u" % (time / (GST_SECOND * 60 * 60), + (time / (GST_SECOND * 60)) % 60, + (time / GST_SECOND) % 60, + time % GST_SECOND) + + +def look_for_file_in_source_dir(subdir, name): + root_dir = os.path.abspath(os.path.dirname(os.path.join(os.path.dirname(os.path.abspath(__file__))))) + p = os.path.join(root_dir, subdir, name) + if os.path.exists(p): + return p + + return None + + +# Returns the path $top_src_dir/@subdir/@name if running from source, or +# $DATADIR/gstreamer-1.0/validate/@name if not +def get_data_file(subdir, name): + # Are we running from sources? + p = look_for_file_in_source_dir(subdir, name) + if p: + return p + + # Look in system data dirs + p = os.path.join(config.DATADIR, 'gstreamer-1.0', 'validate', name) + if os.path.exists(p): + return p + + return None + +# +# Some utilities to parse gst-validate output # +# + + +def gsttime_from_tuple(stime): + return long((int(stime[0]) * 3600 + int(stime[1]) * 60 + int(stime[2])) * GST_SECOND + int(stime[3])) + +timeregex = re.compile(r'(?P<_0>.+):(?P<_1>.+):(?P<_2>.+)\.(?P<_3>.+)') + + +def parse_gsttimeargs(time): + stime = map(itemgetter(1), sorted( + timeregex.match(time).groupdict().items())) + return long((int(stime[0]) * 3600 + int(stime[1]) * 60 + int(stime[2])) * GST_SECOND + int(stime[3])) + + +def get_duration(media_file): + + duration = 0 + res = '' + try: + res = subprocess.check_output([DISCOVERER_COMMAND, media_file]) + except subprocess.CalledProcessError: + # gst-media-check returns !0 if seeking is not possible, we do not care + # in that case. + pass + + for l in res.split('\n'): + if "Duration: " in l: + duration = parse_gsttimeargs(l.replace("Duration: ", "")) + break + + return duration + + +def get_scenarios(): + GST_VALIDATE_COMMAND = "gst-validate-1.0" + os.system("%s --scenarios-defs-output-file %s" % (GST_VALIDATE_COMMAND, + )) diff --git a/validate/launcher/vfb_server.py b/validate/launcher/vfb_server.py new file mode 100644 index 0000000..5a24c58 --- /dev/null +++ b/validate/launcher/vfb_server.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python2 +# +# Copyright (c) 2015,Thibault Saunier +# +# This program 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.1 of the License, or (at your option) any later version. +# +# This program 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 program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301, USA. + +import os +import time +import loggable +import subprocess + + +class VirtualFrameBufferServer(loggable.Loggable): + def __init__(self, options): + loggable.Loggable.__init__(self) + self.options = options + + def start(self): + raise NotImplementedError + + def stop(self): + raise NotImplementedError + + +class Xvfb(VirtualFrameBufferServer): + + """ Class to run xvfb in a process.""" + + def __init__(self, options): + VirtualFrameBufferServer.__init__(self, options) + self.display_id = ":27" + self._process = None + self._logsfile = None + self._command = "Xvfb %s -screen 0 1920x1080x24" % self.display_id + + def _check_is_up(self, timeout=60): + """ Check if the xvfb is up, running a simple test based on wget. """ + start = time.time() + while True: + try: + cdisplay = os.environ.get("DISPLAY", None) + os.environ["DISPLAY"] = self.display_id + subprocess.check_output(["xset", "q"], + stderr=self._logsfile) + print("DISPLAY set to %s" % self.display_id) + return True + except subprocess.CalledProcessError: + os.environ["DISPLAY"] = cdisplay + pass + + if time.time() - start > timeout: + return False + + time.sleep(1) + + def start(self): + """ Start xvfb in a subprocess """ + self._logsfile = open(os.path.join(self.options.logsdir, + "xvfb.logs"), 'w+') + if self._check_is_up(timeout=2): + print("xvfb already running") + return (True, None) + + print("Starting xvfb") + try: + self.debug("Launching xvfb: %s (logs in %s)", self._command, self._logsfile) + self._process = subprocess.Popen(self._command.split(" "), + stderr=self._logsfile, + stdout=self._logsfile) + self.debug("Launched xvfb") + + # Dirty way to avoid eating to much CPU... + # good enough for us anyway. + time.sleep(1) + + if self._check_is_up(): + print("Xvfb tarted") + return (True, None) + else: + print("Failed starting xvfb") + self._process.terminate() + self._process = None + except Exception as ex: + return (False, "Could not launch %s %s\n" + "Make sure Xvbf is installed" % (self._command, ex)) + + def stop(self): + """ Stop the xvfb subprocess if running. """ + if self._process: + self._process.terminate() + self._process = None + self.debug("xvfb stopped") + + +def get_virual_frame_buffer_server(options): + """ + Return a VirtualFrameBufferServer + """ + return Xvfb(options) diff --git a/validate/multi-pre-commit.hook b/validate/multi-pre-commit.hook new file mode 100755 index 0000000..4a1e8e0 --- /dev/null +++ b/validate/multi-pre-commit.hook @@ -0,0 +1,43 @@ +#!/bin/sh +# Git pre-commit hook that runs multiple hooks specified in $HOOKS. +# Make sure this script is executable. Bypass hooks with git commit --no-verify. + +# This file is inspired by a set of unofficial pre-commit hooks available +# at github. +# Link: https://github.com/githubbrowser/Pre-commit-hooks +# Contact: David Martin, david.martin.mailbox@googlemail.com + + +########################################################### +# SETTINGS: +# pre-commit hooks to be executed. They should be in the same .git/hooks/ folder +# as this script. Hooks should return 0 if successful and nonzero to cancel the +# commit. They are executed in the order in which they are listed. +########################################################### + +HOOKS="validate/common/hooks/pre-commit.hook validate/pre-commit-python.hook" + +# exit on error +set -e + +echo $PWD + +for hook in $HOOKS +do + echo "Running hook: $hook" + # run hook if it exists + # if it returns with nonzero exit with 1 and thus abort the commit + if [ -f "$PWD/$hook" ]; then + "$PWD/$hook" + if [ $? != 0 ]; then + exit 1 + fi + else + echo "Error: file $hook not found." + echo "Aborting commit. Make sure the hook is at $PWD/$hook and executable." + echo "You can disable it by removing it from the list" + echo "You can skip all pre-commit hooks with --no-verify (not recommended)." + exit 1 + fi +done + diff --git a/validate/pkgconfig/Makefile.am b/validate/pkgconfig/Makefile.am new file mode 100644 index 0000000..b93b5c9 --- /dev/null +++ b/validate/pkgconfig/Makefile.am @@ -0,0 +1,21 @@ +pcfiles = \ + gst-validate-@GST_API_VERSION@.pc + +pcfiles_uninstalled = \ + gst-validate-@GST_API_VERSION@-uninstalled.pc + +all-local: $(pcfiles) $(pcfiles_uninstalled) + +### how to generate pc files +%-@GST_API_VERSION@.pc: %.pc + cp $< $@ +%-@GST_API_VERSION@-uninstalled.pc: %-uninstalled.pc + cp $< $@ + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = $(pcfiles) + +EXTRA_DIST = \ + gst-validate.pc.in \ + gst-validate-uninstalled.pc.in +CLEANFILES = $(pcfiles) $(pcfiles_uninstalled) diff --git a/validate/pkgconfig/gst-validate-uninstalled.pc.in b/validate/pkgconfig/gst-validate-uninstalled.pc.in new file mode 100644 index 0000000..7f77d22 --- /dev/null +++ b/validate/pkgconfig/gst-validate-uninstalled.pc.in @@ -0,0 +1,12 @@ +# the standard variables don't make sense for an uninstalled copy +prefix= +exec_prefix= +libdir=@abs_top_builddir@/ges +includedir=@abs_top_builddir@ + +Name: gst-validate +Description: GStreamer Validate +Version: @VERSION@ +Requires: gstreamer-@GST_API_VERSION@ +Libs: @abs_top_builddir@/gst/validate/libgstvalidate-@GST_API_VERSION@.la +Cflags: -I@abs_top_srcdir@ -I@abs_top_builddir@ diff --git a/validate/pkgconfig/gst-validate.pc.in b/validate/pkgconfig/gst-validate.pc.in new file mode 100644 index 0000000..d136fed --- /dev/null +++ b/validate/pkgconfig/gst-validate.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@/gstreamer-@GST_API_VERSION@ + +Name: gst-validate +Description: Gstreamer Validate +Version: @VERSION@ +Requires: gstreamer-@GST_API_VERSION@ +Libs: -L${libdir} -lgstvalidate-@GST_API_VERSION@ +Cflags: -I${includedir} diff --git a/validate/plugins/Makefile.am b/validate/plugins/Makefile.am new file mode 100644 index 0000000..97d117f --- /dev/null +++ b/validate/plugins/Makefile.am @@ -0,0 +1,9 @@ +SUBDIRS = fault_injection gapplication + +if HAVE_GTK +SUBDIRS += gtk +endif + +if HAVE_CAIRO +SUBDIRS += ssim +endif diff --git a/validate/plugins/fault_injection/Makefile.am b/validate/plugins/fault_injection/Makefile.am new file mode 100644 index 0000000..3a181ff --- /dev/null +++ b/validate/plugins/fault_injection/Makefile.am @@ -0,0 +1,10 @@ +plugin_LTLIBRARIES = libgstvalidatefaultinjection.la + +libgstvalidatefaultinjection_la_SOURCES = \ + socket_interposer.c + +libgstvalidatefaultinjection_la_CFLAGS = $(GST_ALL_CFLAGS) +libgstvalidatefaultinjection_la_LIBADD = $(GST_ALL_LIBS) $(top_builddir)/gst/validate/libgstvalidate-@GST_API_VERSION@.la +libgstvalidatefaultinjection_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(GST_ALL_LDFLAGS) + +CLEANFILES = diff --git a/validate/plugins/fault_injection/socket_interposer.c b/validate/plugins/fault_injection/socket_interposer.c new file mode 100644 index 0000000..a27a429 --- /dev/null +++ b/validate/plugins/fault_injection/socket_interposer.c @@ -0,0 +1,388 @@ +/* GStreamer + * + * Copyright (C) 2014 YouView TV Ltd + * Authors: Mariusz Buras + * Mathieu Duponchelle + * + * socket_interposer.c : overrides for standard socket functions + * + * This library 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.1 of the License, or (at your option) any later version. + * + * This library 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; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#define _GNU_SOURCE + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "../../gst/validate/gst-validate-scenario.h" + +#if defined(__gnu_linux__) && !defined(__ANDROID__) && !defined (ANDROID) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_CALLBACKS (16) + +pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +/* Return 0 to remove the callback immediately */ +typedef int (*socket_interposer_callback) (void *, const void *, size_t); + +struct +{ + socket_interposer_callback callback; + void *userdata; + struct sockaddr_in sockaddr; + int fd; +} callbacks[MAX_CALLBACKS]; + +static int +socket_interposer_remove_callback_unlocked (struct sockaddr_in *addrin, + socket_interposer_callback callback, void *userdata) +{ + size_t i; + for (i = 0; i < MAX_CALLBACKS; i++) { + if (callbacks[i].callback == callback + && callbacks[i].userdata == userdata + && callbacks[i].sockaddr.sin_addr.s_addr == addrin->sin_addr.s_addr + && callbacks[i].sockaddr.sin_port == addrin->sin_port) { + memset (&callbacks[i], 0, sizeof (callbacks[0])); + return 1; + } + } + return 0; +} + +static void +socket_interposer_set_callback (struct sockaddr_in *addrin, + socket_interposer_callback callback, void *userdata) +{ + size_t i; + pthread_mutex_lock (&mutex); + + + socket_interposer_remove_callback_unlocked (addrin, callback, userdata); + for (i = 0; i < MAX_CALLBACKS; i++) { + if (callbacks[i].callback == NULL) { + callbacks[i].callback = callback; + callbacks[i].userdata = userdata; + memcpy (&callbacks[i].sockaddr, addrin, sizeof (struct sockaddr_in)); + callbacks[i].fd = -1; + break; + } + } + pthread_mutex_unlock (&mutex); +} + +int +connect (int socket, const struct sockaddr_in *addrin, socklen_t address_len) +{ + size_t i; + int override_errno = 0; + typedef ssize_t (*real_connect_fn) (int, const struct sockaddr_in *, + socklen_t); + static real_connect_fn real_connect = 0; + ssize_t ret = 0; + + pthread_mutex_lock (&mutex); + + for (i = 0; i < MAX_CALLBACKS; i++) { + if (callbacks[i].sockaddr.sin_addr.s_addr == addrin->sin_addr.s_addr + && callbacks[i].sockaddr.sin_port == addrin->sin_port) { + + callbacks[i].fd = socket; + + if (callbacks[i].callback) { + int ret = callbacks[i].callback (callbacks[i].userdata, NULL, + 0); + if (ret != 0) + override_errno = ret; + else /* Remove the callback */ + memset (&callbacks[i], 0, sizeof (callbacks[0])); + } + + break; + } + } + + pthread_mutex_unlock (&mutex); + + if (!real_connect) { + real_connect = (real_connect_fn) dlsym (RTLD_NEXT, "connect"); + } + + if (!override_errno) { + ret = real_connect (socket, addrin, address_len); + } else { + // override errno + errno = override_errno; + ret = -1; + } + return ret; +} + +ssize_t +send (int socket, const void *buffer, size_t len, int flags) +{ + size_t i; + int override_errno = 0; + typedef ssize_t (*real_send_fn) (int, const void *, size_t, int); + ssize_t ret; + static real_send_fn real_send = 0; + + pthread_mutex_lock (&mutex); + for (i = 0; i < MAX_CALLBACKS; i++) { + if (callbacks[i].fd != 0 && callbacks[i].fd == socket) { + int ret = callbacks[i].callback (callbacks[i].userdata, buffer, + len); + + if (ret != 0) + override_errno = ret; + else /* Remove the callback */ + memset (&callbacks[i], 0, sizeof (callbacks[0])); + + break; + } + } + pthread_mutex_unlock (&mutex); + + if (!real_send) { + real_send = (real_send_fn) dlsym (RTLD_NEXT, "send"); + } + + ret = real_send (socket, buffer, len, flags); + + // override errno + if (override_errno != 0) { + errno = override_errno; + ret = -1; + } + + return ret; + +} + +ssize_t +recv (int socket, void *buffer, size_t length, int flags) +{ + size_t i; + int old_errno; + typedef ssize_t (*real_recv_fn) (int, void *, size_t, int); + ssize_t ret; + static real_recv_fn real_recv = 0; + + if (!real_recv) { + real_recv = (real_recv_fn) dlsym (RTLD_NEXT, "recv"); + } + + ret = real_recv (socket, buffer, length, flags); + old_errno = errno; + + pthread_mutex_lock (&mutex); + for (i = 0; i < MAX_CALLBACKS; i++) { + if (callbacks[i].fd != 0 && callbacks[i].fd == socket) { + int newerrno = callbacks[i].callback (callbacks[i].userdata, buffer, + ret); + + // override errno + if (newerrno != 0) { + old_errno = newerrno; + ret = -1; + } else { /* Remove the callback */ + memset (&callbacks[i], 0, sizeof (callbacks[0])); + } + + break; + } + } + pthread_mutex_unlock (&mutex); + + errno = old_errno; + + return ret; +} + +struct errno_entry +{ + const gchar *str; + int _errno; +}; + +static struct errno_entry errno_map[] = { + {"ECONNABORTED", ECONNABORTED}, + {"ECONNRESET", ECONNRESET}, + {"ENETRESET", ENETRESET}, + {"ECONNREFUSED", ECONNREFUSED}, + {"EHOSTUNREACH", EHOSTUNREACH}, + {"EHOSTDOWN", EHOSTDOWN}, + {NULL, 0}, +}; + +static int +socket_callback_ (GstValidateAction * action, const void *buff, size_t len) +{ + gint times; + gint real_errno; + + gst_structure_get_int (action->structure, "times", ×); + gst_structure_get_int (action->structure, "real_errno", &real_errno); + + times -= 1; + gst_structure_set (action->structure, "times", G_TYPE_INT, times, NULL); + if (times <= 0) { + gst_validate_action_set_done (action); + return 0; + } + + return real_errno; +} + +static gint +errno_string_to_int (const gchar * errno_str) +{ + gint i; + + for (i = 0; errno_map[i]._errno; i += 1) { + if (!g_ascii_strcasecmp (errno_map[i].str, errno_str)) + return errno_map[i]._errno; + } + + return 0; +} + +static gboolean +_fault_injector_loaded (void) +{ + const gchar *ld_preload = g_getenv ("LD_PRELOAD"); + + + return (ld_preload && strstr (ld_preload, "libfaultinjection-1.0.so")); +} + +static gboolean +_execute_corrupt_socket_recv (GstValidateScenario * scenario, + GstValidateAction * action) +{ + struct sockaddr_in addr = + { AF_INET, htons (42), {htonl (INADDR_LOOPBACK)}, {0} }; + gint server_port, times; + const gchar *errno_str; + gint real_errno; + + if (!_fault_injector_loaded ()) { + GST_ERROR + ("The fault injector wasn't preloaded, can't execute socket recv corruption\n" + "You should set LD_PRELOAD to the path of libfaultinjection.so"); + return FALSE; + } + + if (!gst_structure_get_int (action->structure, "port", &server_port)) { + GST_ERROR ("could not get port to corrupt recv on."); + return FALSE; + } + + if (!gst_structure_get_int (action->structure, "times", ×)) { + gst_structure_set (action->structure, "times", G_TYPE_INT, 1, NULL); + } + + errno_str = gst_structure_get_string (action->structure, "errno"); + if (!errno_str) { + GST_ERROR ("Could not get errno string"); + return FALSE; + } + + real_errno = errno_string_to_int (errno_str); + + if (real_errno == 0) { + GST_ERROR ("unrecognized errno"); + return FALSE; + } + + gst_structure_set (action->structure, "real_errno", G_TYPE_INT, real_errno, + NULL); + + addr.sin_port = htons (server_port); + + socket_interposer_set_callback (&addr, + (socket_interposer_callback) socket_callback_, action); + + return GST_VALIDATE_EXECUTE_ACTION_ASYNC; +} + +static gboolean +socket_interposer_init (GstPlugin * plugin) +{ +/* *INDENT-OFF* */ + gst_validate_register_action_type_dynamic (plugin, "corrupt-socket-recv", + GST_RANK_PRIMARY, + _execute_corrupt_socket_recv, ((GstValidateActionParameter[]) { + { + .name = "port", + .description = "The port the socket to be corrupted listens on", + .mandatory = TRUE, + .types = "int", + .possible_variables = NULL, + }, + { + .name = "errno", + .description = "errno to set when failing", + .mandatory = TRUE, + .types = "string", + }, + { + .name = "times", + .description = "Number of times to corrupt recv, default is one", + .mandatory = FALSE, + .types = "int", + .possible_variables = NULL, + .def = "1", + }, + {NULL} + }), + "corrupt the next socket receive", GST_VALIDATE_ACTION_TYPE_ASYNC); +/* *INDENT-ON* */ + + return TRUE; +} + +#else /* No LD_PRELOAD tricks on Windows */ + +static gboolean +socket_interposer_init (GstPlugin * plugin) +{ + return TRUE; +} + +#endif + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + faultinjector, + "Fault injector plugin for GstValidate", + socket_interposer_init, VERSION, "LGPL", GST_PACKAGE_NAME, + GST_PACKAGE_ORIGIN) diff --git a/validate/plugins/gapplication/Makefile.am b/validate/plugins/gapplication/Makefile.am new file mode 100644 index 0000000..20ce6a0 --- /dev/null +++ b/validate/plugins/gapplication/Makefile.am @@ -0,0 +1,11 @@ +plugin_LTLIBRARIES = libgstvalidategapplication.la + +libgstvalidategapplication_la_SOURCES = \ + gstvalidategapplication.c + +libgstvalidategapplication_la_CFLAGS = $(GST_ALL_CFLAGS) $(GIO_CFLAGS) +libgstvalidategapplication_la_LIBADD = $(GST_ALL_LIBS) $(top_builddir)/gst/validate/libgstvalidate-@GST_API_VERSION@.la $(GIO_LIBS) +libgstvalidategapplication_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(GST_ALL_LDFLAGS) $(GIO_LDFLAGS) + +CLEANFILES = + diff --git a/validate/plugins/gapplication/gstvalidategapplication.c b/validate/plugins/gapplication/gstvalidategapplication.c new file mode 100644 index 0000000..45ff1b8 --- /dev/null +++ b/validate/plugins/gapplication/gstvalidategapplication.c @@ -0,0 +1,80 @@ +/* GStreamer + * + * Copyright (C) 2015 Raspberry Pi Foundation + * Author: Thibault Saunier + * + * gstvalidategapplication.c: GstValidateAction overrides for gapplication + * + * This library 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.1 of the License, or (at your option) any later version. + * + * This library 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; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#define _GNU_SOURCE + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "../../gst/validate/validate.h" +#include "../../gst/validate/gst-validate-scenario.h" +#include "../../gst/validate/gst-validate-utils.h" + + +static gboolean +_execute_stop (GstValidateScenario * scenario, GstValidateAction * action) +{ + g_application_quit (g_application_get_default ()); + + return TRUE; +} + +static gboolean +gst_validate_gapplication_init (GstPlugin * plugin) +{ + GList *config, *tmp; + const gchar *appname; + + config = gst_validate_plugin_get_config (plugin); + + if (!config) + return TRUE; + + for (tmp = config; tmp; tmp = tmp->next) { + appname = gst_structure_get_string (tmp->data, "application-name"); + } + + if (appname && g_strcmp0 (g_get_prgname (), appname)) { + GST_INFO_OBJECT (plugin, "App: %s is not %s", g_get_prgname (), appname); + return TRUE; + } + + gst_validate_register_action_type_dynamic (plugin, "stop", + GST_RANK_PRIMARY, _execute_stop, NULL, + "Sets the pipeline state to NULL", + GST_VALIDATE_ACTION_TYPE_NO_EXECUTION_NOT_FATAL | + GST_VALIDATE_ACTION_TYPE_DOESNT_NEED_PIPELINE); + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + gstvalidategapplication, + "GstValidate plugin to run validate on gapplication", + gst_validate_gapplication_init, VERSION, "LGPL", GST_PACKAGE_NAME, + GST_PACKAGE_ORIGIN) diff --git a/validate/plugins/gtk/Makefile.am b/validate/plugins/gtk/Makefile.am new file mode 100644 index 0000000..f049a27 --- /dev/null +++ b/validate/plugins/gtk/Makefile.am @@ -0,0 +1,11 @@ +plugin_LTLIBRARIES = libgstvalidategtk.la + +libgstvalidategtk_la_SOURCES = gstvalidategtk.c + +libgstvalidategtk_la_CFLAGS = $(GST_ALL_CFLAGS) $(GTK_CFLAGS) +libgstvalidategtk_la_LIBADD = $(GST_ALL_LIBS) $(top_builddir)/gst/validate/libgstvalidate-@GST_API_VERSION@.la $(GTK_LIBS) +libgstvalidategtk_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(GST_ALL_LDFLAGS) $(GTK_LDFLAGS) + +CLEANFILES = + + diff --git a/validate/plugins/gtk/gstvalidategtk.c b/validate/plugins/gtk/gstvalidategtk.c new file mode 100644 index 0000000..6adc112 --- /dev/null +++ b/validate/plugins/gtk/gstvalidategtk.c @@ -0,0 +1,519 @@ +/* GStreamer + * + * Copyright (C) 2015 Raspberry Pi Foundation + * Author: Thibault Saunier + * + * gstvalidategtk.c: GstValidateActionTypes to use with gtk applications + * + * This library 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.1 of the License, or (at your option) any later version. + * + * This library 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; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#define _GNU_SOURCE + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "../../gst/validate/gst-validate-report.h" +#include "../../gst/validate/gst-validate-reporter.h" +#include "../../gst/validate/validate.h" +#include "../../gst/validate/gst-validate-scenario.h" +#include "../../gst/validate/gst-validate-utils.h" + +#define ACTION_GDKEVENTS_QUARK g_quark_from_static_string("ACTION_GDKEVENTS_QUARK") +static GList *awaited_actions = NULL; /* A list of GstValidateAction to be executed */ + +static const gchar * +get_widget_name (GtkWidget * widget) +{ + const gchar *name = NULL; + + if (GTK_IS_BUILDABLE (widget)) + name = gtk_buildable_get_name (GTK_BUILDABLE (widget)); + + if (!name) { + name = gtk_widget_get_name (widget); + } + + return name; +} + +static GdkEventType +get_event_type (GstValidateScenario * scenario, GstValidateAction * action) +{ + guint type; + const gchar *etype_str = gst_structure_get_string (action->structure, "type"); + + if (!etype_str) + return GDK_NOTHING; + + if (gst_validate_utils_enum_from_str (GDK_TYPE_EVENT_TYPE, etype_str, &type)) + return type; + + GST_VALIDATE_REPORT (scenario, + g_quark_from_static_string ("scenario::execution-error"), + "Uknown event type %s, the string should look like the ones defined in " + "gdk_event_type_get_type", etype_str); + + return -2; +} + +static GdkDevice * +get_device (GstValidateAction * action, GdkInputSource input_source) +{ + GList *tmp, *devices; + GdkDevice *device = NULL; + GdkDeviceManager *dev_manager; + + dev_manager = gdk_display_get_device_manager (gdk_display_get_default ()); + devices = + gdk_device_manager_list_devices (dev_manager, GDK_DEVICE_TYPE_MASTER); + + for (tmp = devices; tmp; tmp = tmp->next) { + if (gdk_device_get_source (tmp->data) == input_source) { + device = tmp->data; + break; + } + } + + g_list_free (devices); + + return device; +} + +static GdkEvent * +_create_key_event (GdkWindow * window, GdkEventType etype, guint keyval, + guint hw_keycode, guint state, GdkDevice * device) +{ + GdkEvent *event = gdk_event_new (etype); + GdkEventKey *kevent = (GdkEventKey *) event; + + kevent->window = g_object_ref (window); + kevent->send_event = TRUE; + kevent->time = GDK_CURRENT_TIME; + kevent->keyval = keyval; + kevent->hardware_keycode = hw_keycode; + kevent->state = state; + + gdk_event_set_device (event, device); + + return event; +} + +static GList * +_create_keyboard_events (GstValidateAction * action, + GdkWindow * window, const gchar * keyname, const gchar * string, + GdkEventType etype) +{ + guint *keys; + GList *events = NULL; + GdkDevice *device = NULL; + + if (etype == GDK_NOTHING) { + etype = GDK_KEY_PRESS; + } else if (etype != GDK_KEY_PRESS && etype != GDK_KEY_RELEASE) { + GST_VALIDATE_REPORT (action->scenario, + g_quark_from_static_string ("scenario::execution-error"), + "GdkEvent type %s does not work with the 'keys' parametter", + gst_structure_get_string (action->structure, "type")); + + return NULL; + } + + + device = get_device (action, GDK_SOURCE_KEYBOARD); + if (device == NULL) { + GST_VALIDATE_REPORT (action->scenario, + g_quark_from_static_string ("scenario::execution-error"), + "Could not find a keyboard device"); + + return NULL; + } + + if (keyname) { + guint keyval, state; + + gtk_accelerator_parse_with_keycode (keyname, &keyval, &keys, &state); + events = + g_list_append (events, _create_key_event (window, etype, keyval, + keys[0], state, device)); + } else if (string) { + gint i; + + for (i = 0; string[i]; i++) { + gint n_keys; + GdkKeymapKey *kmaps; + guint keyval = gdk_unicode_to_keyval (string[i]); + + gdk_keymap_get_entries_for_keyval (gdk_keymap_get_default (), + keyval, &kmaps, &n_keys); + + events = + g_list_append (events, _create_key_event (window, etype, keyval, + kmaps[0].keycode, 0, device)); + } + } + + return events; +} + +typedef struct +{ + gchar **widget_paths; + gint current_index; + GtkWidget *widget; + gboolean found; +} WidgetNameWidget; + +static GtkWidget *_find_widget (GtkContainer * container, + WidgetNameWidget * res); + +static gboolean +_widget_has_name (GtkWidget * widget, gchar * name) +{ + if (g_strcmp0 (get_widget_name (GTK_WIDGET (widget)), name) == 0) { + return TRUE; + } + + return FALSE; +} + +static void +_find_widget_cb (GtkWidget * child, WidgetNameWidget * res) +{ + if (res->found) { + return; + } + + if (_widget_has_name (child, res->widget_paths[res->current_index])) { + res->current_index++; + + if (res->widget_paths[res->current_index] == NULL) { + res->widget = child; + res->found = TRUE; + GST_ERROR ("%p GOT IT!!! %s", child, + gtk_buildable_get_name (GTK_BUILDABLE (child))); + } else if (GTK_CONTAINER (child)) { + res->widget = _find_widget (GTK_CONTAINER (child), res); + } + + } else { + if (GTK_IS_CONTAINER (child)) { + res->widget = _find_widget (GTK_CONTAINER (child), res); + } + } + +} + +static GtkWidget * +_find_widget (GtkContainer * container, WidgetNameWidget * res) +{ + if (res->found) + return res->widget; + + if (_widget_has_name (GTK_WIDGET (container), + res->widget_paths[res->current_index])) { + res->current_index++; + + if (res->widget_paths[res->current_index] == NULL) + return GTK_WIDGET (container); + } + + gtk_container_forall (container, (GtkCallback) _find_widget_cb, res); + + if (res->widget) { + res->current_index++; + + if (res->widget_paths[res->current_index + 1] == NULL) + return res->widget; + + if (GTK_IS_CONTAINER (res->widget)) + _find_widget (GTK_CONTAINER (res->widget), res); + } + + return res->widget; +} + + +static void +_find_button (GtkWidget * widget, GtkWidget ** button) +{ + if (GTK_IS_BUTTON (widget)) + *button = widget; +} + +/* Copy pasted from gtk+/gtk/gtktestutils.c */ +static GSList * +test_find_widget_input_windows (GtkWidget * widget, gboolean input_only) +{ + GdkWindow *window; + GList *node, *children; + GSList *matches = NULL; + gpointer udata; + + window = gtk_widget_get_window (widget); + + gdk_window_get_user_data (window, &udata); + if (udata == widget && (!input_only || (GDK_IS_WINDOW (window) + && gdk_window_is_input_only (GDK_WINDOW (window))))) + matches = g_slist_prepend (matches, window); + children = gdk_window_get_children (gtk_widget_get_parent_window (widget)); + for (node = children; node; node = node->next) { + gdk_window_get_user_data (node->data, &udata); + if (udata == widget && (!input_only || (GDK_IS_WINDOW (node->data) + && gdk_window_is_input_only (GDK_WINDOW (node->data))))) + matches = g_slist_prepend (matches, node->data); + } + return g_slist_reverse (matches); +} + +static GdkWindow * +widget_get_window (GtkWidget * widget) +{ + GdkWindow *res = NULL; + GSList *iwindows = test_find_widget_input_windows (widget, FALSE); + + if (!iwindows) + iwindows = test_find_widget_input_windows (widget, TRUE); + + if (iwindows) + res = iwindows->data; + + g_slist_free (iwindows); + + return res; +} + +static GdkWindow * +get_window (GstValidateScenario * scenario, GstValidateAction * action, + const gchar * widget_name) +{ + GList *tmptoplevel; + GdkWindow *res = NULL; + gchar **widget_paths = NULL; + + GList *toplevels = gtk_window_list_toplevels (); + + if (!widget_name) + widget_name = gst_structure_get_string (action->structure, "widget-name"); + + if (!toplevels) { + GST_VALIDATE_REPORT (scenario, + g_quark_from_static_string ("scenario::execution-error"), + "No Gtk topelevel window found, can not sent GdkEvent"); + + return NULL; + } + + if (!widget_name) { + res = gtk_widget_get_window (toplevels->data); + + goto done; + } + + widget_paths = g_strsplit (widget_name, "/", -1); + + for (tmptoplevel = toplevels; tmptoplevel; tmptoplevel = tmptoplevel->next) { + GtkWidget *widget; + WidgetNameWidget wn; + + wn.widget_paths = widget_paths; + wn.current_index = 0; + wn.found = FALSE; + wn.widget = NULL; + + widget = _find_widget (tmptoplevel->data, &wn); + if (widget) { + if (GTK_IS_TOOL_BUTTON (widget)) { + GST_ERROR ("IS TOOL BUTTON"); + gtk_container_forall (GTK_CONTAINER (widget), + (GtkCallback) _find_button, &widget); + } + + res = widget_get_window (widget); + break; + } + } + +done: + g_list_free (toplevels); + + return res; +} + +static GstValidateActionReturn +_put_events (GstValidateAction * action, GList * events) +{ + GList *tmp; + + if (events == NULL) + return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED; + + gst_mini_object_set_qdata (GST_MINI_OBJECT (action), ACTION_GDKEVENTS_QUARK, + events, NULL); + awaited_actions = g_list_append (awaited_actions, action); + + for (tmp = events; tmp; tmp = tmp->next) { + gdk_event_put (tmp->data); + } + + return GST_VALIDATE_EXECUTE_ACTION_ASYNC; +} + +static gboolean +_execute_put_events (GstValidateScenario * scenario, GstValidateAction * action) +{ + GdkEventType etype; + const gchar *keys, *string; + + GList *events = NULL; + GdkWindow *window = get_window (scenario, action, NULL); + + if (!window) + return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED; + + etype = get_event_type (scenario, action); + if (etype == -2) + return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED; + + keys = gst_structure_get_string (action->structure, "keys"); + string = gst_structure_get_string (action->structure, "string"); + if (keys || string) { + events = _create_keyboard_events (action, window, keys, string, etype); + + return _put_events (action, events); + } + + GST_VALIDATE_REPORT (scenario, + g_quark_from_static_string ("scenario::execution-error"), + "Action parametters not supported yet"); + + return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED; +} + +static void +_process_event (GdkEvent * event, gpointer data) +{ + GList *tmp; + GdkEvent *done_event = NULL; + GstValidateAction *action = NULL; + + for (tmp = awaited_actions; tmp; tmp = tmp->next) { + GstValidateAction *tmp_action = tmp->data; + GdkEvent *awaited_event = + ((GList *) gst_mini_object_get_qdata (GST_MINI_OBJECT (tmp_action), + ACTION_GDKEVENTS_QUARK))->data; + + if (awaited_event->type == event->type + && ((GdkEventAny *) event)->window == + ((GdkEventAny *) awaited_event)->window) { + + switch (awaited_event->type) { + case GDK_KEY_PRESS: + case GDK_KEY_RELEASE: + if (event->key.keyval == awaited_event->key.keyval) { + done_event = awaited_event; + action = tmp_action; + } + break; + default: + g_assert_not_reached (); + } + } + } + + if (done_event) { + GList *awaited_events = gst_mini_object_get_qdata (GST_MINI_OBJECT (action), + ACTION_GDKEVENTS_QUARK); + + awaited_events = g_list_remove (awaited_events, done_event); + gdk_event_free (done_event); + gst_mini_object_set_qdata (GST_MINI_OBJECT (action), ACTION_GDKEVENTS_QUARK, + awaited_events, NULL); + + if (awaited_events == NULL) { + awaited_actions = g_list_remove (awaited_actions, action); + gst_validate_action_set_done (action); + } + } + + gtk_main_do_event (event); +} + +static gboolean +gst_validate_gtk_init (GstPlugin * plugin) +{ + gdk_event_handler_set (_process_event, NULL, NULL); + +/* *INDENT-OFF* */ + gst_validate_register_action_type_dynamic (plugin, "gtk-put-event", + GST_RANK_PRIMARY, _execute_put_events, ((GstValidateActionParameter[]) { + { + .name = "keys", + .description = "The keyboard keys to be used for the event, parsed" + " with gtk_accelerator_parse_with_keycode, so refer to its documentation" + " for more information", + .mandatory = FALSE, + .types = "string", + .possible_variables = NULL, + }, + { + .name = "string", + .description = "The string to be 'written' by the keyboard" + " sending KEY_PRESS GdkEvents", + .mandatory = FALSE, + .types = "string", + .possible_variables = NULL, + }, + { + .name = "type", + .description = "The event type to get executed. " + "the string should look like the ones in GdkEventType but without" + " the leading 'GDK_'. It is not mandatory as it can be computed from" + " other present fields (e.g, an action with 'keys' will concider the type" + " as 'key_pressed' by default).", + .mandatory = FALSE, + .types = "string", + }, + { + .name = "widget-name", + .description = "The name of the target GdkWidget of the GdkEvent" + ". That widget has to contain a GdkWindow. If not specified," + " the event will be sent to the first toplevel window", + .mandatory = FALSE, + .types = "string", + .possible_variables = NULL, + }, + {NULL} + }), + "Put a GdkEvent on the event list using gdk_put_event", + GST_VALIDATE_ACTION_TYPE_NO_EXECUTION_NOT_FATAL | + GST_VALIDATE_ACTION_TYPE_DOESNT_NEED_PIPELINE); +/* *INDENT-ON* */ + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + gstvalidategtk, + "GstValidate plugin to execute action specific to the Gtk toolkit", + gst_validate_gtk_init, VERSION, "LGPL", GST_PACKAGE_NAME, + GST_PACKAGE_ORIGIN) diff --git a/validate/plugins/ssim/Makefile.am b/validate/plugins/ssim/Makefile.am new file mode 100644 index 0000000..71618ce --- /dev/null +++ b/validate/plugins/ssim/Makefile.am @@ -0,0 +1,12 @@ +plugin_LTLIBRARIES = libgstvalidatessim.la + +libgstvalidatessim_la_SOURCES = gstvalidatessim.c + +libgstvalidatessim_la_CFLAGS = $(GST_ALL_CFLAGS) -I$(top_builddir)/gst-libs/gst/video/ $(CAIRO_CFLAGS) $(GST_VIDEO_CFLAGS) +libgstvalidatessim_la_LIBADD = $(GST_ALL_LIBS) $(top_builddir)/gst/validate/libgstvalidate-@GST_API_VERSION@.la $(top_builddir)/gst-libs/gst/video/libgstvalidatevideo-@GST_API_VERSION@.la $(CAIRO_LIBS) $(GST_VIDEO_LIBS) +libgstvalidatessim_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(GST_ALL_LDFLAGS) $(CAIR_LDFLAGS) $(GST_VIDEO_LDFLAGS) + +CLEANFILES = + + + diff --git a/validate/plugins/ssim/gstvalidatessim.c b/validate/plugins/ssim/gstvalidatessim.c new file mode 100644 index 0000000..56fad97 --- /dev/null +++ b/validate/plugins/ssim/gstvalidatessim.c @@ -0,0 +1,816 @@ +/* GStreamer + * + * Copyright (C) 2015 Raspberry Pi Foundation + * Author: Thibault Saunier + * + * gstvalidatessim.c: GstValidateActionTypes to use with ssim applications + * + * This library 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.1 of the License, or (at your option) any later version. + * + * This library 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; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/** + * SECTION:validate-ssim + * @short_description: GstValidate plugin to detect frame corruptions + * + * GstValidate plugin to run the ssim algorithm on the buffers flowing in the + * pipeline to find regressions and detect frame corruptions. + * It allows you to generate image files from the buffers flowing in the pipeline + * (either as raw in the many formats supported by GStreamer or as png) and then + * check them against pre generated, reference images. + * + * The ssim algorithm will set a value of 1.0 when images are perfectly identical, + * and -1.0 if they have nothing in common. By default we consider images as similar + * if they have at least a ssim value of 0.95 but you can override it defining the value + * under which the test will be considered as failed. + * + * Errors are reported on the GstValidate reporting system. You can also ask + * the plugin to generate grey scale output images. Those will be named in a way + * that should lets you precisely see where and how the test failed. + * + * # Configuration + * + * The configuration of the plugin is done through a validate configuration file, + * specified with the %GST_VALIDATE_CONFIG environment variable. Each line starting + * with 'ssim,' will configure the ssim plugin. In practice each configuration statement + * will lead to the creation of a #GstValidateOverride object which will then dump + * image files and if wanted compare those with a set of reference images. + * + * The following parameters can be passed in the configuration file: + * - element-classification: The target element classification as define in + * gst_element_class_set_metadata + * - output-dir: The directory in which the image files will be saved + * - min-avg-priority: (default 0.95): The minimum average similarity + * under which we consider the test as failing + * - min-lowest-priority: (default 1): The minimum 'lowest' similarity + * under which we consider the test as failing + * - reference-images-dir: Define the directory in which the files to be + * compared can be found + * - result-output-dir: The folder in which to store resulting grey scale + * images when the test failed. In that folder you will find images + * with the structural difference between the expected result and the actual + * result. + * - output-video-format: The format in which you want the images to be saved + * - reference-video-format: The format in which the reference images are stored + * - check-recurrence: The recurrence in seconds (as float) the frames should + * be dumped and checked.By default it is GST_CLOCK_TIME_NONE, meaning each + * and every frame is checked. Not that in any case, after a discontinuity + * in the stream (after a seek or a change in the video format for example) + * a check is done. And if recurrence == 0, images will be checked only after + * such discontinuity + * - is-config: Property letting the plugin know that the config line is exclusively + * used to configure the following configuration expressions. In practice this + * means that it will change the default values for the other configuration + * expressions. + * + * # Example # + * + * Let's take a special configuration where we want to compare frames that are + * outputted by a video decoder with the ones after a agingtv element we would + * call my_agingtv. We force to check one frame every 5.0 seconds only (with + * check-recurrence=5.0) so the test is fast. + * + * The configuration file: + * |[ + * core, action=set-property, target-element-klass=Sink, property-name=sync, property-value=false + * + * ssim, is-config=true, output-video-format="I420", reference-video-format="I420" + * ssim, element-classification="Video/Decoder", output-dir=/tmp/test/before-agingtv/ + * ssim, element-name=my_agingtv, output-dir=/tmp/test/after-agingtv/, \ + * reference-images-dir=/tmp/test/before-agingtv/, \ + * result-output-dir=/tmp/test/failures, check-recurrence=5.0 + * ]| + * + * Save that content in a file called check_agingtv_ssim.config + * + * ## Launch the pipeline + * |[ + * GST_VALIDATE_CONFIG=check_agingtv_ssim.config gst-validate-1.0-debug uridecodebin uri=file://a/file ! videoconvert ! agingtv name=my_agingtv ! videoconvert ! autovideosink + * ]| + */ + +#define _GNU_SOURCE + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include +#include + +#include "../../gst-libs/gst/video/gstvalidatessim.h" +#include "../../gst/validate/gst-validate-report.h" +#include "../../gst/validate/gst-validate-pad-monitor.h" +#include "../../gst/validate/gst-validate-reporter.h" +#include "../../gst/validate/validate.h" +#include "../../gst/validate/gst-validate-scenario.h" +#include "../../gst/validate/gst-validate-utils.h" + +#define SSIM_WRONG_FORMAT g_quark_from_static_string ("validatessim::wrong-format") +#define SSIM_CONVERSION_ERROR g_quark_from_static_string ("validatessim::conversion-error") +#define SSIM_SAVING_ERROR g_quark_from_static_string ("validatessim::saving-error") +#define MONITOR_DATA g_quark_from_static_string ("validate-ssim-monitor-data") + +typedef struct _ValidateSsimOverridePriv ValidateSsimOverridePriv; + +typedef struct +{ + GstValidateOverride parent; + + ValidateSsimOverridePriv *priv; + +} ValidateSsimOverride; + +typedef struct +{ + GstValidateOverrideClass parent; + +} ValidateSsimOverrideClass; + +typedef struct +{ + gchar *path; + GstClockTime position; + guint width, height; +} Frame; + +static void +free_frame (Frame * frame) +{ + g_free (frame->path); +} + + +static GType validate_ssim_override_get_type (void); + +#define VALIDATE_SSIM_OVERRIDE_TYPE (validate_ssim_override_get_type ()) +#define VALIDATE_SSIM_OVERRIDE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), VALIDATE_SSIM_OVERRIDE_TYPE, ValidateSsimOverride)) +#define VALIDATE_SSIM_OVERRIDE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), VALIDATE_SSIM_OVERRIDE_TYPE, ValidateSsimOverrideClass)) +#define IS_VALIDATE_SSIM_OVERRIDE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), VALIDATE_SSIM_OVERRIDE_TYPE)) +#define IS_VALIDATE_SSIM_OVERRIDE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), VALIDATE_SSIM_OVERRIDE_TYPE)) +#define VALIDATE_SSIM_OVERRIDE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), VALIDATE_SSIM_OVERRIDE_TYPE, ValidateSsimOverrideClass)) + +/* *INDENT-OFF* */ +G_DEFINE_TYPE (ValidateSsimOverride, validate_ssim_override, GST_TYPE_VALIDATE_OVERRIDE) +/* *INDENT-ON* */ + +struct _ValidateSsimOverridePriv +{ + gchar *outdir; + gchar *result_outdir; + GstStructure *config; + + gboolean is_attached; + + GstVideoConverter *converter; + GstCaps *last_caps; + GstVideoInfo in_info; + GstVideoInfo out_info; + + GArray *frames; + GstClockTime recurrence; + GstClockTime last_dump_position; + + /* Always used in the streaming thread */ + gboolean needs_reconfigure; + GstVideoFormat save_format; + const gchar *ext; + GstVideoFormat ref_format; + const gchar *ref_ext; +}; + +static void +runner_stopping (GstValidateRunner * runner, ValidateSsimOverride * self) +{ + GstValidateSsim *ssim; + + guint i, nfiles; + gfloat mssim = 0, lowest = 1, highest = -1, total_avg = 0; + gint npassed = 0, nfailures = 0; + gdouble min_avg_similarity = 0.95, min_lowest_similarity = -1.0, + min_avg = 1.0, min_min = 1.0; + const gchar *compared_files_dir = + gst_structure_get_string (self->priv->config, + "reference-images-dir"); + + if (!compared_files_dir) { + return; + } + + gst_validate_printf (self, + "Running frame comparison between images from %s and %s" "%s%s.\n", + compared_files_dir, self->priv->outdir, + self->priv->result_outdir ? ". Issues can be visialized in " : + " (set 'result-output-dir' in the config file to visualize the result)", + self->priv->result_outdir ? self->priv->result_outdir : ""); + + gst_structure_get_double (self->priv->config, "min-avg-priority", + &min_avg_similarity); + gst_structure_get_double (self->priv->config, "min-lowest-priority", + &min_lowest_similarity); + + ssim = + gst_validate_ssim_new (runner, min_avg_similarity, min_lowest_similarity); + + nfiles = self->priv->frames->len; + for (i = 0; i < nfiles; i++) { + Frame *frame = &g_array_index (self->priv->frames, Frame, i); + gchar *refname, *ref_path, *bname = g_path_get_basename (frame->path); + + if (self->priv->ref_format == GST_VIDEO_FORMAT_ENCODED) + refname = g_strdup_printf ("*.%s", self->priv->ref_ext); + else + refname = g_strdup_printf ("*.%dx%d.%s", frame->width, frame->height, + self->priv->ref_ext); + + ref_path = g_build_path (G_DIR_SEPARATOR_S, compared_files_dir, + refname, NULL); + + if (!gst_validate_ssim_compare_image_files (ssim, ref_path, frame->path, + &mssim, &lowest, &highest, self->priv->result_outdir)) + nfailures++; + else + npassed++; + + min_avg = MIN (min_avg, mssim); + min_min = MIN (lowest, min_min); + total_avg += mssim; + gst_validate_printf (NULL, + "\n", + GST_TIME_ARGS (frame->position), GST_TIME_ARGS (GST_CLOCK_TIME_NONE), + i + 1, nfiles, mssim, lowest, npassed, nfailures); + + g_free (bname); + } + + gst_validate_printf (NULL, + "\nAverage similarity: %f, min_avg: %f, min_min: %f\n", + total_avg / nfiles, min_avg, min_min); +} + +static void +_runner_set (GObject * object, GParamSpec * pspec, gpointer user_data) +{ + ValidateSsimOverride *self = VALIDATE_SSIM_OVERRIDE (object); + + self->priv->is_attached = TRUE; + + g_signal_connect (gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER + (self)), "stopping", G_CALLBACK (runner_stopping), self); +} + +static ValidateSsimOverride * +validate_ssim_override_new (GstStructure * config) +{ + const gchar *format; + ValidateSsimOverride *self = g_object_new (VALIDATE_SSIM_OVERRIDE_TYPE, NULL); + + self->priv->outdir = + g_strdup (gst_structure_get_string (config, "output-dir")); + + if (self->priv->outdir == NULL) { + gchar *template = g_build_filename (g_get_tmp_dir (), + "validatessim-XXXXXX", NULL); + self->priv->outdir = g_mkdtemp (template); + } + + if (!g_file_test (self->priv->outdir, G_FILE_TEST_IS_DIR)) { + if (g_mkdir_with_parents (self->priv->outdir, 0755) != 0) { + + GST_ERROR ("Could not create directory %s", self->priv->outdir); + + g_object_unref (self); + + return NULL; + } + } + + gst_validate_printf (self, "Using %s as output directory\n", + self->priv->outdir); + + self->priv->config = gst_structure_copy (config); + self->priv->result_outdir = + g_strdup (gst_structure_get_string (config, "result-output-dir")); + + format = gst_structure_get_string (config, "output-video-format"); + if (!format) { + self->priv->save_format = GST_VIDEO_FORMAT_ENCODED; + self->priv->ext = "png"; + } else { + self->priv->save_format = gst_video_format_from_string (format); + self->priv->ext = format; + } + + if (self->priv->save_format == GST_VIDEO_FORMAT_UNKNOWN) { + GST_ERROR ("Uknown video format: %s", format); + + gst_object_unref (self); + + return NULL; + } + + format = gst_structure_get_string (config, "reference-video-format"); + if (!format) { + self->priv->ref_ext = "png"; + self->priv->ref_format = GST_VIDEO_FORMAT_ENCODED; + } else { + self->priv->ref_format = gst_video_format_from_string (format); + if (self->priv->ref_format == GST_VIDEO_FORMAT_UNKNOWN) { + GST_ERROR ("Uknown video format: %s", format); + + gst_object_unref (self); + + return NULL; + } + + self->priv->ref_ext = format; + } + + gst_validate_utils_get_clocktime (config, "check-recurrence", + &self->priv->recurrence); + + g_signal_connect (self, "notify::validate-runner", G_CALLBACK (_runner_set), + NULL); + + return self; +} + + +static gboolean +_can_attach (GstValidateOverride * override, GstValidateMonitor * monitor) +{ + guint i; + GstPad *pad; + GstCaps *template_caps; + GstElement *element; + GstStructure *structure; + + if (VALIDATE_SSIM_OVERRIDE (override)->priv->is_attached) { + GST_ERROR_OBJECT (override, "Already attached"); + + return FALSE; + } + + if (!GST_IS_VALIDATE_PAD_MONITOR (monitor)) { + return FALSE; + } + + pad = GST_VALIDATE_PAD_MONITOR_GET_PAD (monitor); + element = gst_validate_monitor_get_element (monitor); + if ((gst_validate_element_has_klass (element, "Converter") || + gst_validate_element_has_klass (element, "Filter")) && + GST_PAD_IS_SINK (pad)) { + GST_INFO_OBJECT (override, "Not attaching on filter sinkpads"); + + return FALSE; + } + + template_caps = GST_PAD_TEMPLATE_CAPS (GST_PAD_PAD_TEMPLATE (pad)); + for (i = 0; i < gst_caps_get_size (template_caps); i++) { + structure = gst_caps_get_structure (template_caps, i); + if (gst_structure_has_name (structure, "video/x-raw")) { + GST_INFO_OBJECT (override, "Wrapping %" GST_PTR_FORMAT, pad); + + gst_validate_reporter_set_name (GST_VALIDATE_REPORTER (override), + g_strdup_printf ("ssim-override-%s", + gst_validate_reporter_get_name (GST_VALIDATE_REPORTER + (monitor)))); + + return TRUE; + } + } + + return FALSE; +} + +static void +_finalize (GObject * object) +{ + ValidateSsimOverridePriv *priv = VALIDATE_SSIM_OVERRIDE (object)->priv; + + if (priv->converter) + gst_video_converter_free (priv->converter); + + if (priv->last_caps) + gst_caps_unref (priv->last_caps); + + g_free (priv->outdir); + g_free (priv->result_outdir); + g_array_unref (priv->frames); + + if (priv->config) + gst_structure_free (priv->config); +} + +static void +validate_ssim_override_class_init (ValidateSsimOverrideClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = _finalize; + + if (!gst_validate_is_initialized ()) + return; + + GST_VALIDATE_OVERRIDE_CLASS (klass)->can_attach = _can_attach; + + gst_validate_issue_register (gst_validate_issue_new (SSIM_WRONG_FORMAT, + "The ValidateSSim plugin can not work with a video format", + "The GstValidate ssim plugin was not able to work" + " with a video format that flowed in the pipeline." + " Make sure you properly configured the plugin", + GST_VALIDATE_REPORT_LEVEL_CRITICAL)); + + gst_validate_issue_register (gst_validate_issue_new (SSIM_CONVERSION_ERROR, + "The ValidateSSim plugin could not convert a frame in the needed format", + "The GstValidate ssim plugin needs to convert the frame in a colorspace" + " it can handle, but it was not possible.", + GST_VALIDATE_REPORT_LEVEL_CRITICAL)); + + gst_validate_issue_register (gst_validate_issue_new (SSIM_SAVING_ERROR, + "The ValidateSSim plugin could not save PNG file", + "The ValidateSSim plugin could not save PNG file", + GST_VALIDATE_REPORT_LEVEL_CRITICAL)); + + g_type_class_add_private (klass, sizeof (ValidateSsimOverridePriv)); +} + +static void +validate_ssim_override_init (ValidateSsimOverride * self) +{ + self->priv = + G_TYPE_INSTANCE_GET_PRIVATE (self, VALIDATE_SSIM_OVERRIDE_TYPE, + ValidateSsimOverridePriv); + + self->priv->needs_reconfigure = TRUE; + self->priv->frames = g_array_new (TRUE, TRUE, sizeof (Frame)); + g_array_set_clear_func (self->priv->frames, (GDestroyNotify) free_frame); +} + +static gboolean +_set_videoconvert (ValidateSsimOverride * o, + GstValidatePadMonitor * pad_monitor) +{ + GstCaps *caps; + GstVideoFormat format; + ValidateSsimOverridePriv *priv = o->priv; + GstPad *pad = GST_VALIDATE_PAD_MONITOR_GET_PAD (pad_monitor); + + caps = gst_pad_get_current_caps (pad); + gst_caps_replace (&priv->last_caps, caps); + + gst_video_info_init (&priv->in_info); + gst_video_info_init (&priv->out_info); + if (priv->converter) { + gst_video_converter_free (priv->converter); + priv->converter = NULL; + } + + if (!gst_video_info_from_caps (&priv->in_info, priv->last_caps)) { + GST_VALIDATE_REPORT (o, SSIM_WRONG_FORMAT, + "The format %" GST_PTR_FORMAT " is not supported" + " by the plugin", pad_monitor->last_caps); + + return FALSE; + } + + if (GST_VIDEO_INFO_HAS_ALPHA (&priv->in_info)) + format = GST_VIDEO_FORMAT_BGRA; + else + format = GST_VIDEO_FORMAT_BGRx; + + if (priv->in_info.finfo->format == format) { + GST_INFO_OBJECT (o, "No conversion needed"); + + return TRUE; + } + + if (priv->save_format != GST_VIDEO_FORMAT_ENCODED) + format = priv->save_format; + + gst_video_info_set_format (&priv->out_info, format, + priv->in_info.width, priv->in_info.height); + priv->out_info.fps_d = priv->in_info.fps_d; + priv->out_info.fps_n = priv->in_info.fps_n; + + priv->converter = gst_video_converter_new (&priv->in_info, + &priv->out_info, NULL); + + return TRUE; + +} + +static gboolean +has_frame (ValidateSsimOverride * self, gchar * name) +{ + guint i; + GArray *frames = self->priv->frames; + + for (i = 0; i < frames->len; ++i) { + if (g_strcmp0 (g_array_index (frames, Frame, i).path, name) == 0) + return TRUE; + } + + return FALSE; +} + + +static gchar * +_get_filename (ValidateSsimOverride * self, GstValidatePadMonitor * monitor, + GstClockTime position) +{ + gint i = 0; + gchar *outname = NULL, *s; + + if (self->priv->save_format == GST_VIDEO_FORMAT_ENCODED) + s = g_strdup_printf ("%" GST_TIME_FORMAT ".%s", GST_TIME_ARGS (position), + self->priv->ext); + else + s = g_strdup_printf ("%" GST_TIME_FORMAT ".%dx%d.%s", + GST_TIME_ARGS (position), + self->priv->out_info.width, + self->priv->out_info.height, self->priv->ext); + + outname = g_build_path (G_DIR_SEPARATOR_S, self->priv->outdir, s, NULL); + + g_free (s); + while (has_frame (self, outname)) { + g_free (outname); + if (self->priv->save_format == GST_VIDEO_FORMAT_ENCODED) + s = g_strdup_printf ("%" GST_TIME_FORMAT "-%d.%s", + GST_TIME_ARGS (position), i++, self->priv->ext); + else + s = g_strdup_printf ("%" GST_TIME_FORMAT "-%d.%dx%d.%s", + GST_TIME_ARGS (position), i++, self->priv->out_info.width, + self->priv->out_info.height, self->priv->ext); + + outname = g_build_path (G_DIR_SEPARATOR_S, self->priv->outdir, s, NULL); + g_free (s); + } + + return outname; +} + +static gboolean +_should_dump_buffer (ValidateSsimOverride * self, + GstValidatePadMonitor * pad_monitor, GstClockTime position) +{ + ValidateSsimOverridePriv *priv = self->priv; + + if (!GST_CLOCK_TIME_IS_VALID (priv->recurrence)) + return TRUE; + + if (priv->needs_reconfigure) + return TRUE; + + /* recurence 0 means, dump exclusively on reconfiguration */ + if (priv->recurrence == 0) + return FALSE; + + if (ABS (position - priv->last_dump_position) >= priv->recurrence) + return TRUE; + + return FALSE; +} + +static gboolean +_save_frame (ValidateSsimOverride * self, GstVideoFrame * frame, + const gchar * outname) +{ + gboolean res = TRUE; + cairo_status_t status; + cairo_surface_t *surface; + GError *error = NULL; + + if (self->priv->save_format == GST_VIDEO_FORMAT_ENCODED) { + surface = + cairo_image_surface_create_for_data (GST_VIDEO_FRAME_PLANE_DATA (frame, + 0), CAIRO_FORMAT_RGB24, GST_VIDEO_FRAME_WIDTH (frame), + GST_VIDEO_FRAME_HEIGHT (frame), GST_VIDEO_FRAME_PLANE_STRIDE (frame, + 0)); + + if ((status = cairo_surface_write_to_png (surface, outname)) != + CAIRO_STATUS_SUCCESS) { + GST_VALIDATE_REPORT (self, SSIM_SAVING_ERROR, + "Could not save %s" " cairo status is %s", outname, + cairo_status_to_string (status)); + + res = FALSE; + } + + cairo_surface_destroy (surface); + + return res; + } + + if (!g_file_set_contents (outname, + GST_VIDEO_FRAME_PLANE_DATA (frame, 0), + GST_VIDEO_FRAME_SIZE (frame), &error)) { + GST_VALIDATE_REPORT (self, SSIM_SAVING_ERROR, + "Could not save %s error: %s", outname, error->message); + res = FALSE; + } + + return res; +} + +static void +_handle_buffer (GstValidateOverride * override, + GstValidatePadMonitor * pad_monitor, GstBuffer * buffer) +{ + gchar *outname = NULL; + GstVideoFrame frame; + Frame iframe; + + ValidateSsimOverride *o = VALIDATE_SSIM_OVERRIDE (override); + ValidateSsimOverridePriv *priv = o->priv; + + GstClockTime running_time, position; + + running_time = gst_segment_to_running_time (&pad_monitor->segment, + GST_FORMAT_TIME, GST_BUFFER_PTS (buffer)); + position = gst_segment_to_position (&pad_monitor->segment, + GST_FORMAT_TIME, running_time); + + if (!_should_dump_buffer (o, pad_monitor, position)) { + GST_LOG_OBJECT (override, "Not dumping buffer: %" GST_TIME_FORMAT, + GST_TIME_ARGS (position)); + + return; + } + + if (priv->needs_reconfigure) { + priv->needs_reconfigure = !_set_videoconvert (o, pad_monitor); + } + + if (priv->converter) { + GstVideoFrame inframe; + GstBuffer *outbuf; + + if (!gst_video_frame_map (&inframe, &priv->in_info, buffer, GST_MAP_READ)) { + GST_VALIDATE_REPORT (o, SSIM_CONVERSION_ERROR, + "Could not map the videoframe %p", buffer); + + return; + } + + outbuf = gst_buffer_new_allocate (NULL, priv->out_info.size, NULL); + if (!gst_video_frame_map (&frame, &priv->out_info, outbuf, GST_MAP_WRITE)) { + GST_VALIDATE_REPORT (o, SSIM_CONVERSION_ERROR, + "Could not map the outbuffer %p", outbuf); + + gst_buffer_unref (outbuf); + return; + } + gst_buffer_unref (outbuf); + gst_video_converter_frame (priv->converter, &inframe, &frame); + gst_video_frame_unmap (&inframe); + } else { + if (!gst_video_frame_map (&frame, &priv->in_info, buffer, GST_MAP_WRITE)) { + GST_VALIDATE_REPORT (o, SSIM_CONVERSION_ERROR, + "Could not map the buffer %p", buffer); + return; + } + } + + outname = _get_filename (o, pad_monitor, position); + if (_save_frame (o, &frame, outname)) { + priv->last_dump_position = position; + + iframe.position = position; + iframe.path = outname; + iframe.width = priv->in_info.width; + iframe.height = priv->in_info.height; + g_array_append_val (priv->frames, iframe); + } + + gst_video_frame_unmap (&frame); +} + +static void +_handle_event (GstValidateOverride * override, + GstValidateMonitor * pad_monitor, GstEvent * event) +{ + ValidateSsimOverride *self = VALIDATE_SSIM_OVERRIDE (override); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_STOP: + self->priv->needs_reconfigure = TRUE; + break; + case GST_EVENT_CAPS: + self->priv->needs_reconfigure = TRUE; + break; + default: + break; + } +} + +static gboolean +_map_confg (GQuark field_id, GValue * value, GstStructure * structure) +{ + if (!gst_structure_id_has_field (structure, field_id)) + gst_structure_id_set_value (structure, field_id, value); + + return TRUE; +} + +static gboolean +gst_validate_ssim_init (GstPlugin * plugin) +{ + GList *tmp, *config; + GstStructure *config_structure = NULL; + + if (!gst_validate_is_initialized ()) + return FALSE; + + config = gst_validate_plugin_get_config (plugin); + for (tmp = config; tmp; tmp = tmp->next) { + gboolean is_config; + + if (gst_structure_get_boolean (tmp->data, "is-config", &is_config)) { + if (is_config) { + config_structure = tmp->data; + break; + } + } + } + + for (tmp = config; tmp; tmp = tmp->next) { + const gchar *name = gst_structure_get_string (tmp->data, "element-name"); + const gchar *target_element_classification = + gst_structure_get_string (tmp->data, "element-classification"); + + if (tmp->data == config_structure) + continue; + + if (config_structure) { + gst_structure_map_in_place (config_structure, + (GstStructureMapFunc) _map_confg, tmp->data); + } + if ((name || target_element_classification)) { + GstValidateOverride *override = + GST_VALIDATE_OVERRIDE (validate_ssim_override_new (tmp->data)); + + if (override == NULL) { + GST_ERROR ("Could not create override with config %" + GST_PTR_FORMAT, tmp->data); + + continue; + } + + override->buffer_probe_handler = + (GstValidateOverrideBufferHandler) _handle_buffer; + override->buffer_handler = + (GstValidateOverrideBufferHandler) _handle_buffer; + override->event_handler = (GstValidateOverrideEventHandler) _handle_event; + + if (target_element_classification) + gst_validate_override_register_by_klass (target_element_classification, + override); + else if (name) + gst_validate_override_register_by_name (name, override); + else + g_assert_not_reached (); + + } else { + GST_ERROR ("Wrong configuration '%" GST_PTR_FORMAT + "'element-classification' and output-dir are mandatory fields", + tmp->data); + g_assert_not_reached (); + } + } + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + ssim, + "GstValidate plugin to run the ssim algorithm on raw" + " video buffers. It allows you to generate png files" + "\n " + " and then check them against pre generated, reference images." + "\n " + " The following parametters can be passed in the configuration file:" + "\n " + " 'element-classification': The target element classification as define in gst_element_class_set_metadata" + "\n " + " 'output-dir': The directory in which the image files will be saved'" + "\n", + gst_validate_ssim_init, VERSION, "LGPL", GST_PACKAGE_NAME, + GST_PACKAGE_ORIGIN) diff --git a/validate/pre-commit-python.hook b/validate/pre-commit-python.hook new file mode 100755 index 0000000..1c0efb6 --- /dev/null +++ b/validate/pre-commit-python.hook @@ -0,0 +1,82 @@ +#!/usr/bin/env python2 +import os +import subprocess +import sys +import tempfile + +NOT_PEP8_COMPLIANT_MESSAGE_PRE = \ + "Your code is not fully pep8 compliant and contains"\ + " the following coding style issues:\n\n" + +NOT_PEP8_COMPLIANT_MESSAGE_POST = \ + "Please fix these errors and commit again, you can do so "\ + "from the root directory automatically like this, assuming the whole "\ + "file is to be commited:" + +NO_PEP8_MESSAGE = \ + "You should install the pep8 style checker to be able"\ + " to commit in this repo.\nIt allows us to garantee that "\ + "anything that is commited respects the pep8 coding style "\ + "standard.\nYou can install it:\n"\ + " * on ubuntu, debian: $sudo apt-get install pep8 \n"\ + " * on fedora: #yum install python-pep8 \n"\ + " * on arch: #pacman -S pep8-python3 \n"\ + " * or add the official pep8 from http://www.python.org/dev/peps/pep-0008/"\ + " in your $PATH" + + +def system(*args, **kwargs): + kwargs.setdefault('stdout', subprocess.PIPE) + proc = subprocess.Popen(args, **kwargs) + out, err = proc.communicate() + if type(out) == bytes: + out = out.decode() + return out + + +def copy_files_to_tmp_dir(files): + tempdir = tempfile.mkdtemp() + for name in files: + filename = os.path.join(tempdir, name) + filepath = os.path.dirname(filename) + if not os.path.exists(filepath): + os.makedirs(filepath) + with open(filename, 'w') as f: + system('git', 'show', ':' + name, stdout=f) + + return tempdir + + +def main(): + modified_files = system('git', 'diff-index', '--cached', + '--name-only', 'HEAD', '--diff-filter=ACMR').split("\n")[:-1] + non_compliant_files = [] + output_message = None + + for modified_file in modified_files: + try: + if not modified_file.endswith(".py"): + continue + pep8_errors = system('pep8', '--repeat', '--ignore', 'E501,E128', modified_file) + if pep8_errors: + if output_message is None: + output_message = NOT_PEP8_COMPLIANT_MESSAGE_PRE + output_message += pep8_errors + non_compliant_files.append(modified_file) + except OSError: + output_message = NO_PEP8_MESSAGE + break + + if output_message: + print output_message + if non_compliant_files: + print NOT_PEP8_COMPLIANT_MESSAGE_POST + for non_compliant_file in non_compliant_files: + print "autopep8 -i ", non_compliant_file, "; git add ", \ + non_compliant_file + print "git commit" + sys.exit(1) + + +if __name__ == '__main__': + main() diff --git a/validate/tests/Makefile.am b/validate/tests/Makefile.am new file mode 100644 index 0000000..8bbfc8f --- /dev/null +++ b/validate/tests/Makefile.am @@ -0,0 +1,10 @@ +if HAVE_GST_CHECK +CHECK_SUBDIRS= check +else +CHECK_SUBDIRS= +endif + +SUBDIRS= $(CHECK_SUBDIRS) + +DIST_SUBDIRS = check + diff --git a/validate/tests/check/Makefile.am b/validate/tests/check/Makefile.am new file mode 100644 index 0000000..dd79b59 --- /dev/null +++ b/validate/tests/check/Makefile.am @@ -0,0 +1,98 @@ +include $(top_srcdir)/common/check.mak + +TESTS_ENVIRONMENT = + +plugindir = $(libdir)/gstreamer-@GST_API_VERSION@ + +# override to _not_ install the test plugins +install-pluginLTLIBRARIES: + +# the core dumps of some machines have PIDs appended +CLEANFILES = core.* test-registry.* *.gcno *.gcda + +common_cflags=-I$(top_srcdir) $(GST_PLUGINS_BASE_CFLAGS) $(GST_OBJ_CFLAGS) \ + $(GST_CHECK_CFLAGS) $(GST_OPTION_CFLAGS) $(GST_CFLAGS) +common_ldadd=$(top_builddir)/gst/validate/libgstvalidate-@GST_API_VERSION@.la \ + $(GST_PLUGINS_BASE_LIBS) -lgstpbutils-$(GST_API_VERSION) \ + $(GST_OBJ_LIBS) $(GST_CHECK_LIBS) + +testutils_noisnt_libraries=libtestutils.la +testutils_noinst_headers=validate/test-utils.h +libtestutils_la_LIBADD=$(common_ldadd) +libtestutils_la_CFLAGS=$(common_cflags) +libtestutils_la_SOURCES=validate/test-utils.c + +SUPPRESSIONS = $(top_srcdir)/common/gst.supp # $(srcdir)/gst-plugins-bad.supp + +clean-local: clean-local-check + +check_PROGRAMS = \ + validate/padmonitor \ + validate/monitoring \ + validate/reporting \ + validate/overrides + +noinst_LTLIBRARIES=$(testutils_noisnt_libraries) +noinst_HEADERS=$(testutils_noinst_headers) + +TESTS = $(check_PROGRAMS) + +AM_CFLAGS = $(common_cflags) -UG_DISABLE_ASSERT -UG_DISABLE_CAST_CHECKS +LDADD = $(common_ldadd) libtestutils.la + +debug: + echo $(COVERAGE_FILES) + echo $(COVERAGE_FILES_REL) + +.PHONY: coverage +if GST_GCOV_ENABLED +# we rebuild a registry and do gst-inspect so that all the get/set codepaths +# are also covered +coverage: + make check + make coverage-report +else +coverage: + echo "You need to configure with --enable-gcov to get coverage data" + exit 1 +endif + +coverage-report: + if test ! -e coverage; then + rm -r coverage + fi + for dir in $(COVERAGE_DIRS); do \ + mkdir -p coverage/$$dir; \ + make -C $(top_builddir)/$$dir gcov; \ + done + for dir in $(COVERAGE_DIRS); do \ + files="`ls $(top_builddir)/$$dir/*.gcov.out 2> /dev/null`"; \ + if test ! -z "$$files"; then \ + perl $(top_srcdir)/common/coverage/coverage-report.pl \ + $(top_builddir)/$$dir/*.gcov.out > \ + coverage/$$dir/index.xml; \ + xsltproc $(top_srcdir)/common/coverage/coverage-report.xsl \ + coverage/$$dir/index.xml > coverage/$$dir/index.html; \ + fi; \ + done + for file in $(COVERAGE_FILES_REL); do \ + echo Generating coverage/$$file.html; \ + perl $(top_srcdir)/common/coverage/coverage-report-entry.pl \ + $(top_builddir)/$$file > coverage/$$file.html; \ + done + +check-integration: integration + CK_DEFAULT_TIMEOUT=20 ./integration + +check-integration-forever: + @while true; do \ + make check-integration \ + CK_DEFAULT_TIMEOUT=20 \ + $* || break; done + +check-integration-gdb: + @$(TESTS_ENVIRONMENT) \ + CK_FORK=no \ + $(LIBTOOL) --mode=execute \ + gdb ./integration + diff --git a/validate/tests/check/validate/monitoring.c b/validate/tests/check/validate/monitoring.c new file mode 100644 index 0000000..0bc6849 --- /dev/null +++ b/validate/tests/check/validate/monitoring.c @@ -0,0 +1,111 @@ +/* GstValidate + * Copyright (C) 2014 Thibault Saunier + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include "test-utils.h" + +GST_START_TEST (monitors_added) +{ + GList *tmp; + GstValidateRunner *runner; + GstValidateMonitor *monitor; + GstElement *pipeline = gst_pipeline_new ("validate-pipeline"); + GstElement *src, *sink; + + src = gst_element_factory_make ("fakesrc", "source"); + sink = gst_element_factory_make ("fakesink", "sink"); + + runner = gst_validate_runner_new (); + fail_unless (GST_IS_VALIDATE_RUNNER (runner)); + + monitor = gst_validate_monitor_factory_create (GST_OBJECT_CAST (pipeline), + runner, NULL); + fail_unless (GST_IS_VALIDATE_BIN_MONITOR (monitor)); + + gst_bin_add_many (GST_BIN (pipeline), src, sink, NULL); + gst_element_link (src, sink); + + /* Check that the elements are properly monitored */ + fail_unless_equals_int (g_list_length (src->srcpads), 1); + for (tmp = src->srcpads; tmp; tmp = tmp->next) + fail_unless (GST_IS_VALIDATE_PAD_MONITOR (g_object_get_data ((GObject *) + tmp->data, "validate-monitor"))); + + fail_unless_equals_int (g_list_length (sink->sinkpads), 1); + for (tmp = sink->sinkpads; tmp; tmp = tmp->next) + fail_unless (GST_IS_VALIDATE_PAD_MONITOR (g_object_get_data ((GObject *) + tmp->data, "validate-monitor"))); + + /* clean up */ + gst_object_unref (pipeline); + gst_object_unref (monitor); + gst_object_unref (runner); +} + +GST_END_TEST; + +GST_START_TEST (monitors_cleanup) +{ + GstElement *src, *sink; + GstValidateRunner *runner; + GstValidateMonitor *monitor, *pmonitor1, *pmonitor2; + + GstElement *pipeline = gst_pipeline_new ("validate-pipeline"); + + src = gst_element_factory_make ("fakesrc", "source"); + sink = gst_element_factory_make ("fakesink", "sink"); + + runner = gst_validate_runner_new (); + monitor = gst_validate_monitor_factory_create (GST_OBJECT_CAST (pipeline), + runner, NULL); + gst_bin_add_many (GST_BIN (pipeline), src, sink, NULL); + gst_element_link (src, sink); + + /* Check cleanup */ + pmonitor1 = + g_object_get_data ((GObject *) src->srcpads->data, "validate-monitor"); + pmonitor2 = + g_object_get_data ((GObject *) sink->sinkpads->data, "validate-monitor"); + check_destroyed (monitor, pmonitor1, pmonitor2, NULL); + check_destroyed (pipeline, src, sink, NULL); +} + +GST_END_TEST; + + +static Suite * +gst_validate_suite (void) +{ + Suite *s = suite_create ("monitoring"); + TCase *tc_chain = tcase_create ("monitoring"); + suite_add_tcase (s, tc_chain); + + gst_validate_init (); + + tcase_add_test (tc_chain, monitors_added); + tcase_add_test (tc_chain, monitors_cleanup); + + gst_validate_deinit (); + return s; +} + +GST_CHECK_MAIN (gst_validate); diff --git a/validate/tests/check/validate/overrides.c b/validate/tests/check/validate/overrides.c new file mode 100644 index 0000000..9683ddb --- /dev/null +++ b/validate/tests/check/validate/overrides.c @@ -0,0 +1,109 @@ +/* GstValidate + * Copyright (C) 2014 Thibault Saunier + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include + +static const gchar *some_overrides = + "change-severity, issue-id=buffer::not-expected-one, new-severity=critical\n" + "change-severity, issue-id=buffer::not-expected-one, new-severity=warning, element-factory-name=queue"; + +static void +_check_message_level (const gchar * factoryname, GstValidateReportLevel level, + const gchar * message_id) +{ + GList *reports; + GstElement *element; + GstValidateRunner *runner; + GstValidateMonitor *monitor; + + element = gst_element_factory_make (factoryname, NULL); + fail_unless (g_setenv ("GST_VALIDATE_REPORTING_DETAILS", "all", TRUE)); + runner = gst_validate_runner_new (); + monitor = + gst_validate_monitor_factory_create (GST_OBJECT (element), runner, NULL); + + GST_VALIDATE_REPORT (monitor, g_quark_from_string (message_id), + "Just some fakery"); + + reports = gst_validate_runner_get_reports (runner); + fail_unless_equals_int (g_list_length (reports), 1); + fail_unless_equals_int (((GstValidateReport *) reports->data)->level, level); + g_list_free_full (reports, (GDestroyNotify) gst_validate_report_unref); + gst_object_unref (element); + gst_object_unref (monitor); + +} + +GST_START_TEST (check_text_overrides) +{ + GstValidateIssue *issue; + gchar *override_filename = + g_strdup_printf ("%s%c%s", g_get_tmp_dir (), G_DIR_SEPARATOR, + "some_overrides"); + + fail_unless (g_file_set_contents (override_filename, + some_overrides, -1, NULL)); + + issue = + gst_validate_issue_from_id (g_quark_from_string + ("buffer::not-expected-one")); + fail_unless (issue != NULL); + + assert_equals_int (issue->default_level, GST_VALIDATE_REPORT_LEVEL_WARNING); + + g_setenv ("GST_VALIDATE_OVERRIDE", override_filename, TRUE); + gst_validate_override_registry_preload (); + assert_equals_int (issue->default_level, GST_VALIDATE_REPORT_LEVEL_CRITICAL); + + /* Check that with a queue, the level of a + * buffer::not-expected-one is WARNING */ + _check_message_level ("queue", GST_VALIDATE_REPORT_LEVEL_WARNING, + "buffer::not-expected-one"); + + /* Check that with an identity, the level of a + * buffer::not-expected-one is CRITICAL */ + _check_message_level ("identity", GST_VALIDATE_REPORT_LEVEL_CRITICAL, + "buffer::not-expected-one"); + + g_remove (override_filename); + g_free (override_filename); +} + +GST_END_TEST; + + +static Suite * +gst_validate_suite (void) +{ + Suite *s = suite_create ("registry"); + TCase *tc_chain = tcase_create ("registry"); + suite_add_tcase (s, tc_chain); + + gst_validate_init (); + + tcase_add_test (tc_chain, check_text_overrides); + + gst_validate_deinit (); + return s; +} + +GST_CHECK_MAIN (gst_validate); diff --git a/validate/tests/check/validate/padmonitor.c b/validate/tests/check/validate/padmonitor.c new file mode 100644 index 0000000..675a10d --- /dev/null +++ b/validate/tests/check/validate/padmonitor.c @@ -0,0 +1,1096 @@ +/* GstValidate + * Copyright (C) 2014 Thibault Saunier + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include "test-utils.h" + +static GstValidateRunner * +_start_monitoring_bin (GstBin * bin) +{ + GstValidateRunner *runner; + GstValidateMonitor *monitor; + + runner = gst_validate_runner_new (); + monitor = + gst_validate_monitor_factory_create (GST_OBJECT (bin), runner, NULL); + + gst_validate_reporter_set_handle_g_logs (GST_VALIDATE_REPORTER (monitor)); + return runner; +} + +static void +_stop_monitoring_bin (GstBin * bin, GstValidateRunner * runner) +{ + GstValidateMonitor *monitor; + + monitor = + (GstValidateMonitor *) g_object_get_data (G_OBJECT (bin), + "validate-monitor"); + ASSERT_OBJECT_REFCOUNT (bin, "bin", 1); + gst_object_unref (bin); + ASSERT_OBJECT_REFCOUNT (monitor, "monitor", 1); + gst_object_unref (monitor); + ASSERT_OBJECT_REFCOUNT (runner, "runner", 1); + gst_object_unref (runner); +} + +static GstValidateMonitor * +_start_monitoring_element (GstElement * element, GstValidateRunner * runner) +{ + GstValidateMonitor *monitor; + + monitor = gst_validate_monitor_factory_create (GST_OBJECT (element), + runner, NULL); + + return monitor; +} + +static void +_check_reports_refcount (GstPad * pad, gint refcount) +{ + GList *tmp, *reports; + GstValidateReporter *reporter = + (GstValidateReporter *) g_object_get_data (G_OBJECT (pad), + "validate-monitor"); + + reports = gst_validate_reporter_get_reports (reporter); + /* We take a ref here */ + refcount += 1; + + for (tmp = reports; tmp; tmp = tmp->next) + fail_unless_equals_int (((GstValidateReport *) tmp->data)->refcount, + refcount); + + g_list_free_full (reports, (GDestroyNotify) gst_validate_report_unref); +} + +GST_START_TEST (buffer_before_segment) +{ + GstPad *srcpad; + GstElement *src, *sink; + GstValidateRunner *runner; + GstValidateReport *report; + GstValidateMonitor *monitor; + GList *reports; + + /* getting an existing element class is cheating, but easier */ + src = gst_element_factory_make ("fakesrc", "fakesrc"); + sink = gst_element_factory_make ("fakesink", "fakesink"); + + fail_unless (gst_element_link (src, sink)); + + fail_unless (g_setenv ("GST_VALIDATE_REPORTING_DETAILS", "all", TRUE)); + runner = gst_validate_runner_new (); + monitor = + gst_validate_monitor_factory_create (GST_OBJECT (src), runner, NULL); + fail_unless (GST_IS_VALIDATE_ELEMENT_MONITOR (monitor)); + + srcpad = gst_element_get_static_pad (src, "src"); + + /* We want to handle the src behaviour ourself */ + fail_unless (gst_pad_activate_mode (srcpad, GST_PAD_MODE_PUSH, TRUE)); + fail_unless_equals_int (gst_element_set_state (sink, GST_STATE_PLAYING), + GST_STATE_CHANGE_ASYNC); + + /* Send a buffer before pushing any segment (FAILS) */ + { + _gst_check_expecting_log = TRUE; + fail_unless_equals_int (gst_pad_push (srcpad, gst_buffer_new ()), + GST_FLOW_OK); + + reports = gst_validate_runner_get_reports (runner); + assert_equals_int (g_list_length (reports), 1); + report = reports->data; + fail_unless_equals_int (report->level, GST_VALIDATE_REPORT_LEVEL_WARNING); + fail_unless_equals_int (report->issue->issue_id, BUFFER_BEFORE_SEGMENT); + g_list_free_full (reports, (GDestroyNotify) gst_validate_report_unref); + } + + /* Setup all needed event and push a new buffer (WORKS) */ + { + _gst_check_expecting_log = FALSE; + gst_check_setup_events (srcpad, src, NULL, GST_FORMAT_TIME); + fail_unless_equals_int (gst_pad_push (srcpad, gst_buffer_new ()), + GST_FLOW_OK); + reports = gst_validate_runner_get_reports (runner); + assert_equals_int (g_list_length (reports), 1); + g_list_free_full (reports, (GDestroyNotify) gst_validate_report_unref); + } + + /* clean up */ + fail_unless (gst_pad_activate_mode (srcpad, GST_PAD_MODE_PUSH, FALSE)); + fail_unless_equals_int (gst_element_set_state (sink, GST_STATE_NULL), + GST_STATE_CHANGE_SUCCESS); + + _check_reports_refcount (srcpad, 2); + gst_object_unref (srcpad); + check_destroyed (src, srcpad, NULL); + check_destroyed (sink, NULL, NULL); + check_destroyed (runner, NULL, NULL); +} + +GST_END_TEST; + +GST_START_TEST (buffer_outside_segment) +{ + GstPad *srcpad; + GstBuffer *buffer; + GstSegment segment; + GstElement *src, *sink; + gchar *fakesrc_klass; + GstValidateReport *report; + GstValidateRunner *runner; + GstValidateMonitor *monitor; + GList *reports; + + /* getting an existing element class is cheating, but easier */ + src = gst_element_factory_make ("fakesrc", "fakesrc"); + sink = gst_element_factory_make ("fakesink", "fakesink"); + + fakesrc_klass = + g_strdup (gst_element_class_get_metadata (GST_ELEMENT_GET_CLASS (src), + "klass")); + + /* Testing if a buffer is outside a segment is only done for buffer outputed + * from decoders for the moment, fake a Decoder so that the test is properly + * executed */ + gst_element_class_add_metadata (GST_ELEMENT_GET_CLASS (src), "klass", + "Decoder"); + + fail_unless (g_setenv ("GST_VALIDATE_REPORTING_DETAILS", "all", TRUE)); + runner = gst_validate_runner_new (); + monitor = + gst_validate_monitor_factory_create (GST_OBJECT (src), runner, NULL); + gst_validate_reporter_set_handle_g_logs (GST_VALIDATE_REPORTER (monitor)); + + srcpad = gst_element_get_static_pad (src, "src"); + fail_unless (GST_IS_VALIDATE_PAD_MONITOR (g_object_get_data ((GObject *) + srcpad, "validate-monitor"))); + + fail_unless (gst_pad_activate_mode (srcpad, GST_PAD_MODE_PUSH, TRUE)); + fail_unless_equals_int (gst_element_set_state (sink, GST_STATE_PLAYING), + GST_STATE_CHANGE_ASYNC); + + gst_segment_init (&segment, GST_FORMAT_TIME); + segment.start = 0; + segment.stop = GST_SECOND; + fail_unless (gst_pad_push_event (srcpad, + gst_event_new_stream_start ("the-stream"))); + fail_unless (gst_pad_push_event (srcpad, gst_event_new_segment (&segment))); + + /* Pushing a buffer that is outside the segment */ + { + buffer = gst_buffer_new (); + GST_BUFFER_PTS (buffer) = 10 * GST_SECOND; + GST_BUFFER_DURATION (buffer) = GST_SECOND; + fail_unless (gst_pad_push (srcpad, buffer)); + + reports = gst_validate_runner_get_reports (runner); + assert_equals_int (g_list_length (reports), 1); + report = reports->data; + fail_unless_equals_int (report->level, GST_VALIDATE_REPORT_LEVEL_ISSUE); + fail_unless_equals_int (report->issue->issue_id, BUFFER_IS_OUT_OF_SEGMENT); + g_list_free_full (reports, (GDestroyNotify) gst_validate_report_unref); + } + + /* Pushing a buffer inside the segment */ + { + fail_unless (gst_pad_push (srcpad, gst_buffer_new ())); + reports = gst_validate_runner_get_reports (runner); + assert_equals_int (g_list_length (reports), 1); + g_list_free_full (reports, (GDestroyNotify) gst_validate_report_unref); + } + + + /* clean up */ + fail_unless (gst_pad_activate_mode (srcpad, GST_PAD_MODE_PUSH, FALSE)); + gst_object_unref (srcpad); + + gst_element_class_add_metadata (GST_ELEMENT_GET_CLASS (src), "klass", + fakesrc_klass); + g_free (fakesrc_klass); + gst_object_unref (src); + gst_object_unref (runner); + + fail_unless_equals_int (gst_element_set_state (sink, GST_STATE_NULL), + GST_STATE_CHANGE_SUCCESS); + gst_object_unref (sink); +} + +GST_END_TEST; + +static void +fake_demuxer_prepare_pads (GstBin * pipeline, GstElement * demux, + GstValidateRunner * runner) +{ + gint i = 0; + GList *tmp; + + fail_unless (g_list_length (demux->srcpads), 3); + + for (tmp = demux->srcpads; tmp; tmp = tmp->next) { + GstPad *new_peer; + gchar *name = g_strdup_printf ("sink-%d", i++); + GstElement *sink = gst_element_factory_make ("fakesink", name); + + gst_bin_add (pipeline, sink); + + new_peer = sink->sinkpads->data; + gst_pad_link (tmp->data, new_peer); + gst_element_set_state (sink, GST_STATE_PLAYING); + gst_pad_activate_mode (tmp->data, GST_PAD_MODE_PUSH, TRUE); + + g_free (name); + } + + fail_unless (gst_pad_activate_mode (demux->sinkpads->data, GST_PAD_MODE_PUSH, + TRUE)); +} + +static GstValidatePadMonitor * +_get_pad_monitor (GstPad * pad) +{ + GstValidatePadMonitor *m = get_pad_monitor (pad); + + gst_object_unref (pad); + + return m; +} + +static void +_test_flow_aggregation (GstFlowReturn flow, GstFlowReturn flow1, + GstFlowReturn flow2, GstFlowReturn demux_flow, gboolean should_fail) +{ + GstPad *srcpad; + GstValidateReport *report; + GstValidatePadMonitor *pmonitor, *pmonitor1, *pmonitor2; + GstElement *demuxer = fake_demuxer_new (); + GstBin *pipeline = GST_BIN (gst_pipeline_new ("validate-pipeline")); + GList *reports; + GstValidateRunner *runner; + + fail_unless (g_setenv ("GST_VALIDATE_REPORTING_DETAILS", "all", TRUE)); + runner = _start_monitoring_bin (pipeline); + + gst_bin_add (pipeline, demuxer); + fake_demuxer_prepare_pads (pipeline, demuxer, runner); + + srcpad = gst_pad_new ("srcpad1", GST_PAD_SRC); + gst_pad_link (srcpad, demuxer->sinkpads->data); + fail_unless (gst_pad_activate_mode (srcpad, GST_PAD_MODE_PUSH, TRUE)); + gst_check_setup_events_with_stream_id (srcpad, demuxer, NULL, + GST_FORMAT_TIME, "the-stream"); + + pmonitor = _get_pad_monitor (gst_pad_get_peer (demuxer->srcpads->data)); + pmonitor1 = + _get_pad_monitor (gst_pad_get_peer (demuxer->srcpads->next->data)); + pmonitor2 = + _get_pad_monitor (gst_pad_get_peer (demuxer->srcpads->next->next->data)); + + pmonitor->last_flow_return = flow; + pmonitor1->last_flow_return = flow1; + pmonitor2->last_flow_return = flow2; + FAKE_DEMUXER (demuxer)->return_value = demux_flow; + + fail_unless_equals_int (gst_pad_push (srcpad, gst_buffer_new ()), demux_flow); + + reports = gst_validate_runner_get_reports (runner); + if (should_fail) { + assert_equals_int (g_list_length (reports), 1); + report = reports->data; + fail_unless_equals_int (report->level, GST_VALIDATE_REPORT_LEVEL_CRITICAL); + fail_unless_equals_int (report->issue->issue_id, WRONG_FLOW_RETURN); + } else { + assert_equals_int (g_list_length (reports), 0); + + } + + g_list_free_full (reports, (GDestroyNotify) gst_validate_report_unref); + clean_bus (GST_ELEMENT (pipeline)); + + gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL); + ASSERT_OBJECT_REFCOUNT (pipeline, "ours", 1); + gst_object_ref (demuxer); + gst_object_ref (pmonitor); + _stop_monitoring_bin (pipeline, runner); + + ASSERT_OBJECT_REFCOUNT (demuxer, "plop", 1); + gst_object_unref (demuxer); + ASSERT_OBJECT_REFCOUNT (pmonitor, "plop", 1); + gst_object_unref (pmonitor); +} + +GST_START_TEST (flow_aggregation) +{ + /* Check the GstFlowCombiner to find the rules */ + + /* Failling cases: */ + _test_flow_aggregation (GST_FLOW_OK, GST_FLOW_OK, + GST_FLOW_ERROR, GST_FLOW_OK, TRUE); + _test_flow_aggregation (GST_FLOW_EOS, GST_FLOW_EOS, + GST_FLOW_EOS, GST_FLOW_OK, TRUE); + _test_flow_aggregation (GST_FLOW_FLUSHING, GST_FLOW_OK, + GST_FLOW_OK, GST_FLOW_OK, TRUE); + _test_flow_aggregation (GST_FLOW_NOT_NEGOTIATED, GST_FLOW_OK, + GST_FLOW_OK, GST_FLOW_OK, TRUE); + + /* Passing cases: */ + _test_flow_aggregation (GST_FLOW_EOS, GST_FLOW_EOS, + GST_FLOW_EOS, GST_FLOW_EOS, FALSE); + _test_flow_aggregation (GST_FLOW_EOS, GST_FLOW_EOS, + GST_FLOW_OK, GST_FLOW_OK, FALSE); + _test_flow_aggregation (GST_FLOW_OK, GST_FLOW_OK, + GST_FLOW_OK, GST_FLOW_EOS, FALSE); + _test_flow_aggregation (GST_FLOW_NOT_NEGOTIATED, GST_FLOW_OK, + GST_FLOW_OK, GST_FLOW_NOT_NEGOTIATED, FALSE); +} + +GST_END_TEST; + +static GstPadProbeReturn +drop_buffers (GstPad * pad, GstPadProbeInfo * info, gpointer unused) +{ + return GST_PAD_PROBE_DROP; +} + +GST_START_TEST (issue_concatenation) +{ + GstPad *srcpad1, *srcpad2, *sinkpad, *funnel_sink1, *funnel_sink2; + GstElement *src1, *src2, *sink, *funnel; + GstValidateRunner *runner; + GstValidatePadMonitor *srcpad_monitor1, *srcpad_monitor2, *sinkpad_monitor; + GstValidatePadMonitor *funnel_sink_monitor1, *funnel_sink_monitor2; + GstSegment segment; + GList *reports; + gint n_reports; + gulong probe_id1, probe_id2; + + fail_unless (g_setenv ("GST_VALIDATE_REPORTING_DETAILS", "subchain", TRUE)); + runner = gst_validate_runner_new (); + + src1 = create_and_monitor_element ("fakesrc", "fakesrc1", runner); + src2 = create_and_monitor_element ("fakesrc", "fakesrc2", runner); + funnel = create_and_monitor_element ("funnel", "funnel", runner); + sink = create_and_monitor_element ("fakesink", "fakesink", runner); + + srcpad1 = gst_element_get_static_pad (src1, "src"); + srcpad_monitor1 = g_object_get_data (G_OBJECT (srcpad1), "validate-monitor"); + srcpad2 = gst_element_get_static_pad (src2, "src"); + srcpad_monitor2 = g_object_get_data (G_OBJECT (srcpad2), "validate-monitor"); + funnel_sink1 = gst_element_get_request_pad (funnel, "sink_%u"); + funnel_sink_monitor1 = + g_object_get_data (G_OBJECT (funnel_sink1), "validate-monitor"); + funnel_sink2 = gst_element_get_request_pad (funnel, "sink_%u"); + funnel_sink_monitor2 = + g_object_get_data (G_OBJECT (funnel_sink2), "validate-monitor"); + sinkpad = gst_element_get_static_pad (sink, "sink"); + sinkpad_monitor = g_object_get_data (G_OBJECT (sinkpad), "validate-monitor"); + + fail_unless (gst_element_link (funnel, sink)); + fail_unless (gst_pad_link (srcpad1, funnel_sink1) == GST_PAD_LINK_OK); + fail_unless (gst_pad_link (srcpad2, funnel_sink2) == GST_PAD_LINK_OK); + + /* There's gonna be some clunkiness in here because of funnel */ + probe_id1 = gst_pad_add_probe (srcpad1, + GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BUFFER_LIST | + GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, (GstPadProbeCallback) drop_buffers, + NULL, NULL); + probe_id2 = + gst_pad_add_probe (srcpad2, + GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BUFFER_LIST | + GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, (GstPadProbeCallback) drop_buffers, + NULL, NULL); + + /* We want to handle the src behaviour ourselves */ + fail_unless (gst_pad_activate_mode (srcpad1, GST_PAD_MODE_PUSH, TRUE)); + fail_unless (gst_pad_activate_mode (srcpad2, GST_PAD_MODE_PUSH, TRUE)); + + /* Setup all needed events */ + gst_segment_init (&segment, GST_FORMAT_TIME); + segment.start = 0; + segment.stop = GST_SECOND; + + fail_unless (gst_pad_push_event (srcpad1, + gst_event_new_stream_start ("the-stream"))); + fail_unless (gst_pad_push_event (srcpad1, gst_event_new_segment (&segment))); + + fail_unless (gst_pad_push_event (srcpad2, + gst_event_new_stream_start ("the-stream"))); + fail_unless (gst_pad_push_event (srcpad2, gst_event_new_segment (&segment))); + + fail_unless_equals_int (gst_element_set_state (funnel, GST_STATE_PLAYING), + GST_STATE_CHANGE_SUCCESS); + fail_unless_equals_int (gst_element_set_state (sink, GST_STATE_PLAYING), + GST_STATE_CHANGE_ASYNC); + + + /* Send an unexpected flush stop */ + _gst_check_expecting_log = TRUE; + fail_unless (gst_pad_push_event (srcpad1, gst_event_new_flush_stop (TRUE))); + + /* The runner only sees one report */ + reports = gst_validate_runner_get_reports (runner); + assert_equals_int (g_list_length (reports), 1); + g_list_free_full (reports, (GDestroyNotify) gst_validate_report_unref); + + /* Each pad monitor on the way actually holds a report */ + n_reports = gst_validate_reporter_get_reports_count ((GstValidateReporter *) + srcpad_monitor1); + fail_unless_equals_int (n_reports, 1); + n_reports = gst_validate_reporter_get_reports_count ((GstValidateReporter *) + sinkpad_monitor); + fail_unless_equals_int (n_reports, 1); + n_reports = gst_validate_reporter_get_reports_count ((GstValidateReporter *) + funnel_sink_monitor1); + fail_unless_equals_int (n_reports, 1); + + /* But not the pad monitor of the other funnel sink */ + n_reports = gst_validate_reporter_get_reports_count ((GstValidateReporter *) + funnel_sink_monitor2); + fail_unless_equals_int (n_reports, 0); + n_reports = gst_validate_reporter_get_reports_count ((GstValidateReporter *) + srcpad_monitor2); + fail_unless_equals_int (n_reports, 0); + + /* Once again but on the other funnel sink */ + fail_unless (gst_pad_push_event (srcpad2, gst_event_new_flush_stop (TRUE))); + + /* The runner now sees two reports */ + reports = gst_validate_runner_get_reports (runner); + assert_equals_int (g_list_length (reports), 2); + g_list_free_full (reports, (GDestroyNotify) gst_validate_report_unref); + + /* These monitors already saw that issue */ + n_reports = gst_validate_reporter_get_reports_count ((GstValidateReporter *) + srcpad_monitor1); + fail_unless_equals_int (n_reports, 1); + n_reports = gst_validate_reporter_get_reports_count ((GstValidateReporter *) + sinkpad_monitor); + fail_unless_equals_int (n_reports, 1); + n_reports = gst_validate_reporter_get_reports_count ((GstValidateReporter *) + funnel_sink_monitor1); + fail_unless_equals_int (n_reports, 1); + + n_reports = gst_validate_reporter_get_reports_count ((GstValidateReporter *) + funnel_sink_monitor2); + fail_unless_equals_int (n_reports, 1); + n_reports = gst_validate_reporter_get_reports_count ((GstValidateReporter *) + srcpad_monitor2); + fail_unless_equals_int (n_reports, 1); + + /* clean up */ + fail_unless (gst_pad_activate_mode (srcpad1, GST_PAD_MODE_PUSH, FALSE)); + fail_unless (gst_pad_activate_mode (srcpad2, GST_PAD_MODE_PUSH, FALSE)); + fail_unless_equals_int (gst_element_set_state (funnel, GST_STATE_NULL), + GST_STATE_CHANGE_SUCCESS); + fail_unless_equals_int (gst_element_set_state (sink, GST_STATE_NULL), + GST_STATE_CHANGE_SUCCESS); + + gst_pad_remove_probe (srcpad1, probe_id1); + gst_pad_remove_probe (srcpad2, probe_id2); + + /* The reporter, the runner */ + _check_reports_refcount (srcpad1, 2); + /* The reporter, the master report */ + _check_reports_refcount (funnel_sink1, 2); + free_element_monitor (src1); + free_element_monitor (src2); + free_element_monitor (funnel); + free_element_monitor (sink); + gst_object_unref (srcpad1); + gst_object_unref (srcpad2); + gst_object_unref (sinkpad); + gst_object_unref (funnel_sink1); + gst_object_unref (funnel_sink2); + check_destroyed (funnel, funnel_sink1, funnel_sink2, NULL); + check_destroyed (src1, srcpad1, NULL); + check_destroyed (src2, srcpad2, NULL); + check_destroyed (sink, sinkpad, NULL); + check_destroyed (runner, NULL, NULL); +} + +GST_END_TEST; + +/* *INDENT-OFF* */ +static const gchar * media_info = +"" +" " +" " +" " /* buffer1 */ +" " /* buffer2 */ +" " /* buffer3 */ +" " /* gonna fail */ +" " /* buffer4 */ +" " /* buffer5 */ +" " /* buffer6 */ +" " /* gonna fail */ +" " +" " +" " +" " +""; +/* *INDENT-ON* */ + +typedef struct _BufferDesc +{ + const gchar *content; + GstClockTime pts; + GstClockTime dts; + GstClockTime duration; + gboolean keyframe; + + gint num_issues; +} BufferDesc; + +static GstBuffer * +_create_buffer (BufferDesc * bdesc) +{ + gchar *tmp = g_strdup (bdesc->content); + GstBuffer *buffer = + gst_buffer_new_wrapped (tmp, strlen (tmp) * sizeof (gchar)); + + GST_BUFFER_DTS (buffer) = bdesc->dts; + GST_BUFFER_PTS (buffer) = bdesc->pts; + GST_BUFFER_DURATION (buffer) = bdesc->duration; + + if (bdesc->keyframe) + GST_BUFFER_FLAG_UNSET (buffer, GST_BUFFER_FLAG_DELTA_UNIT); + else + GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT); + + return buffer; +} + +static void +_check_media_info (GstSegment * segment, BufferDesc * bufs) +{ + GList *reports; + GstEvent *segev; + GstBuffer *buffer; + GstElement *decoder; + GstPad *srcpad, *sinkpad; + GstValidateReport *report; + GstValidateMonitor *monitor; + GstValidateRunner *runner; + GstMediaDescriptor *mdesc; + + GError *err = NULL; + gint i, num_issues = 0; + + fail_unless (g_setenv ("GST_VALIDATE_REPORTING_DETAILS", "all", TRUE)); + runner = gst_validate_runner_new (); + + mdesc = (GstMediaDescriptor *) + gst_media_descriptor_parser_new_from_xml (runner, media_info, &err); + + decoder = fake_decoder_new (); + monitor = _start_monitoring_element (decoder, runner); + gst_validate_monitor_set_media_descriptor (monitor, mdesc); + + srcpad = gst_pad_new ("src", GST_PAD_SRC); + sinkpad = decoder->sinkpads->data; + ASSERT_OBJECT_REFCOUNT (sinkpad, "decoder ref", 1); + fail_unless (gst_pad_activate_mode (srcpad, GST_PAD_MODE_PUSH, TRUE)); + fail_unless_equals_int (gst_element_set_state (decoder, GST_STATE_PLAYING), + GST_STATE_CHANGE_SUCCESS); + + assert_equals_string (gst_pad_link_get_name (gst_pad_link (srcpad, sinkpad)), + gst_pad_link_get_name (GST_PAD_LINK_OK)); + + gst_check_setup_events_with_stream_id (srcpad, decoder, + gst_caps_from_string + ("video/x-raw, width=360, height=42, framerate=24/1, pixel-aspect-ratio =1/1, format=AYUV"), + GST_FORMAT_TIME, "the-stream"); + + + if (segment) { + segev = gst_event_new_segment (segment); + fail_unless (gst_pad_push_event (srcpad, segev)); + } + + for (i = 0; bufs[i].content != NULL; i++) { + BufferDesc *buf = &bufs[i]; + buffer = _create_buffer (buf); + + assert_equals_string (gst_flow_get_name (gst_pad_push (srcpad, buffer)), + gst_flow_get_name (GST_FLOW_OK)); + reports = gst_validate_runner_get_reports (runner); + + num_issues += buf->num_issues; + assert_equals_int (g_list_length (reports), num_issues); + + if (buf->num_issues) { + GList *tmp = g_list_nth (reports, num_issues - buf->num_issues); + + while (tmp) { + report = tmp->data; + + fail_unless_equals_int (report->level, + GST_VALIDATE_REPORT_LEVEL_WARNING); + fail_unless_equals_int (report->issue->issue_id, WRONG_BUFFER); + tmp = tmp->next; + } + } + } + + /* clean up */ + fail_unless (gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, FALSE)); + fail_unless_equals_int (gst_element_set_state (decoder, GST_STATE_NULL), + GST_STATE_CHANGE_SUCCESS); + + gst_object_unref (srcpad); + check_destroyed (decoder, sinkpad, NULL); + check_destroyed (runner, NULL, NULL); +} + +GST_START_TEST (check_media_info) +{ + GstSegment segment; + + +/* *INDENT-OFF* */ + _check_media_info (NULL, + (BufferDesc []) { + { + .content = "buffer1", + .pts = 0, + .dts = 0, + .duration = 1, + .keyframe = TRUE, + .num_issues = 0 + }, + { + .content = "buffer2", + .pts = 1, + .dts = 1, + .duration = 1, + .keyframe = FALSE, + .num_issues = 0 + }, + { + .content = "buffer3", + .pts = 2, + .dts = 2, + .duration = 1, + .keyframe = FALSE, + .num_issues = 0 + }, + { + .content = "fail please", + .pts = 3, + .dts = 3, + .duration = 1, + .keyframe = FALSE, + .num_issues = 1 + }, + { NULL} + }); +/* *INDENT-ON* */ + + gst_segment_init (&segment, GST_FORMAT_TIME); + /* Segment start is 2, the first buffer is expected (first Keyframe) */ + segment.start = 2; + +/* *INDENT-OFF* */ + _check_media_info (&segment, + (BufferDesc []) { + { + .content = "buffer2", /* Wrong checksum */ + .pts = 0, + .dts = 0, + .duration = 1, + .keyframe = TRUE, + .num_issues = 1 + }, + { NULL} + }); +/* *INDENT-ON* */ + + gst_segment_init (&segment, GST_FORMAT_TIME); + /* Segment start is 2, the first buffer is expected (first Keyframe) */ + segment.start = 2; + +/* *INDENT-OFF* */ + _check_media_info (&segment, + (BufferDesc []) { + { /* The right first buffer */ + .content = "buffer1", + .pts = 0, + .dts = 0, + .duration = 1, + .keyframe = TRUE, + .num_issues = 0 + }, + { NULL} + }); +/* *INDENT-ON* */ + + gst_segment_init (&segment, GST_FORMAT_TIME); + /* Segment start is 6, the 4th buffer is expected (first Keyframe) */ + segment.start = 6; + +/* *INDENT-OFF* */ + _check_media_info (&segment, + (BufferDesc []) { + { /* The right fourth buffer */ + .content = "buffer4", + .pts = 4, + .dts = 4, + .duration = 1, + .keyframe = TRUE, + .num_issues = 0 + }, + { NULL} + }); +/* *INDENT-ON* */ + + gst_segment_init (&segment, GST_FORMAT_TIME); + /* Segment start is 6, the 4th buffer is expected (first Keyframe) */ + segment.start = 6; + +/* *INDENT-OFF* */ + _check_media_info (&segment, + (BufferDesc []) { + { /* The sixth buffer... all wrong! */ + .content = "buffer6", + .pts = 6, + .dts = 6, + .duration = 1, + .keyframe = FALSE, + .num_issues = 1 + }, + { NULL} + }); +/* *INDENT-ON* */ +} + +GST_END_TEST; + +GST_START_TEST (caps_events) +{ + GstPad *srcpad, *sinkpad; + GstElement *decoder = fake_decoder_new (); + GstElement *sink = gst_element_factory_make ("fakesink", NULL); + GstBin *pipeline = GST_BIN (gst_pipeline_new ("validate-pipeline")); + GList *reports; + GstValidateReport *report; + GstValidateRunner *runner; + + fail_unless (g_setenv ("GST_VALIDATE_REPORTING_DETAILS", "all", TRUE)); + runner = _start_monitoring_bin (pipeline); + + gst_bin_add_many (pipeline, decoder, sink, NULL); + srcpad = gst_pad_new ("srcpad1", GST_PAD_SRC); + sinkpad = decoder->sinkpads->data; + gst_pad_link (srcpad, sinkpad); + + gst_element_link (decoder, sink); + fail_unless_equals_int (gst_element_set_state (GST_ELEMENT (pipeline), + GST_STATE_PLAYING), GST_STATE_CHANGE_ASYNC); + fail_unless (gst_pad_activate_mode (srcpad, GST_PAD_MODE_PUSH, TRUE)); + + reports = gst_validate_runner_get_reports (runner); + assert_equals_int (g_list_length (reports), 0); + g_list_free_full (reports, (GDestroyNotify) gst_validate_report_unref); + + fail_unless (gst_pad_push_event (srcpad, + gst_event_new_caps (gst_caps_from_string + ("video/x-raw, format=AYUV, width=320, height=240, pixel-aspect-ratio=1/1")))); + reports = gst_validate_runner_get_reports (runner); + + /* Our caps didn't have a framerate, the decoder sink should complain about + * that */ + assert_equals_int (g_list_length (reports), 1); + report = reports->data; + fail_unless_equals_int (report->level, GST_VALIDATE_REPORT_LEVEL_ISSUE); + fail_unless_equals_int (report->issue->issue_id, CAPS_IS_MISSING_FIELD); + g_list_free_full (reports, (GDestroyNotify) gst_validate_report_unref); + + fail_unless (gst_pad_push_event (srcpad, + gst_event_new_caps (gst_caps_from_string + ("video/x-raw, format=AYUV, framerate=24/1, width=(fraction)320, height=240, pixel-aspect-ratio=1/1")))); + + reports = gst_validate_runner_get_reports (runner); + assert_equals_int (g_list_length (reports), 2); + report = reports->next->data; + /* A width isn't supposed to be a fraction */ + fail_unless_equals_int (report->level, GST_VALIDATE_REPORT_LEVEL_WARNING); + fail_unless_equals_int (report->issue->issue_id, CAPS_FIELD_HAS_BAD_TYPE); + + fail_unless (gst_pad_push_event (srcpad, + gst_event_new_caps (gst_caps_from_string + ("video/x-raw, format=AYUV, framerate=24/1, width=320, height=240, pixel-aspect-ratio=1/1")))); + fail_unless (gst_pad_push_event (srcpad, + gst_event_new_caps (gst_caps_from_string + ("video/x-raw, format=AYUV, framerate=24/1, width=320, height=240, pixel-aspect-ratio=1/1")))); + + reports = gst_validate_runner_get_reports (runner); + assert_equals_int (g_list_length (reports), 3); + report = reports->next->next->data; + /* A width isn't supposed to be a fraction */ + fail_unless_equals_int (report->level, GST_VALIDATE_REPORT_LEVEL_WARNING); + /* Pushing the same twice isn't very useful */ + fail_unless_equals_int (report->issue->issue_id, EVENT_CAPS_DUPLICATE); + + + clean_bus (GST_ELEMENT (pipeline)); + + g_list_free_full (reports, (GDestroyNotify) gst_validate_report_unref); + + gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL); + _stop_monitoring_bin (pipeline, runner); +} + +GST_END_TEST; + +GST_START_TEST (eos_without_segment) +{ + GstPad *srcpad, *sinkpad; + GstValidateReport *report; + GstElement *decoder = fake_decoder_new (); + GstElement *sink = gst_element_factory_make ("fakesink", NULL); + GstBin *pipeline = GST_BIN (gst_pipeline_new ("validate-pipeline")); + GList *reports; + GstValidateRunner *runner; + + fail_unless (g_setenv ("GST_VALIDATE_REPORTING_DETAILS", "all", TRUE)); + runner = _start_monitoring_bin (pipeline); + + gst_bin_add_many (pipeline, decoder, sink, NULL); + srcpad = gst_pad_new ("srcpad1", GST_PAD_SRC); + sinkpad = decoder->sinkpads->data; + gst_pad_link (srcpad, sinkpad); + + gst_element_link (decoder, sink); + fail_unless_equals_int (gst_element_set_state (GST_ELEMENT (pipeline), + GST_STATE_PLAYING), GST_STATE_CHANGE_ASYNC); + fail_unless (gst_pad_activate_mode (srcpad, GST_PAD_MODE_PUSH, TRUE)); + + reports = gst_validate_runner_get_reports (runner); + assert_equals_int (g_list_length (reports), 0); + g_list_free_full (reports, (GDestroyNotify) gst_validate_report_unref); + + fail_unless (gst_pad_push_event (srcpad, gst_event_new_eos ())); + reports = gst_validate_runner_get_reports (runner); + + /* Getting the issue on the srcpad -> decoder.sinkpad -> decoder->srcpad */ + assert_equals_int (g_list_length (reports), 3); + report = reports->data; + fail_unless_equals_int (report->level, GST_VALIDATE_REPORT_LEVEL_WARNING); + fail_unless_equals_int (report->issue->issue_id, EVENT_EOS_WITHOUT_SEGMENT); + clean_bus (GST_ELEMENT (pipeline)); + + g_list_free_full (reports, (GDestroyNotify) gst_validate_report_unref); + + gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL); + _stop_monitoring_bin (pipeline, runner); +} + +GST_END_TEST; + +GST_START_TEST (buffer_timestamp_out_of_received_range) +{ + GstPad *srcpad, *sinkpad; + GstElement *decoder = fake_decoder_new (); + GstElement *sink = gst_element_factory_make ("fakesink", NULL); + GstBin *pipeline = GST_BIN (gst_pipeline_new ("validate-pipeline")); + GList *reports; + GstValidateRunner *runner; + GstSegment segment; + GstBuffer *buffer; + GstPad *decoder_srcpad; + GstValidateReport *report; + + fail_unless (g_setenv ("GST_VALIDATE_REPORTING_DETAILS", "all", TRUE)); + runner = _start_monitoring_bin (pipeline); + + gst_bin_add_many (pipeline, decoder, sink, NULL); + srcpad = gst_pad_new ("srcpad1", GST_PAD_SRC); + sinkpad = decoder->sinkpads->data; + gst_pad_link (srcpad, sinkpad); + + gst_element_link (decoder, sink); + ASSERT_SET_STATE (GST_ELEMENT (pipeline), GST_STATE_PLAYING, + GST_STATE_CHANGE_ASYNC); + fail_unless (gst_pad_activate_mode (srcpad, GST_PAD_MODE_PUSH, TRUE)); + + gst_segment_init (&segment, GST_FORMAT_TIME); + segment.start = 0; + segment.stop = GST_SECOND; + fail_unless (gst_pad_push_event (srcpad, + gst_event_new_stream_start ("the-stream"))); + fail_unless (gst_pad_push_event (srcpad, gst_event_new_segment (&segment))); + + { + buffer = gst_buffer_new (); + GST_BUFFER_PTS (buffer) = 0 * GST_SECOND; + GST_BUFFER_DURATION (buffer) = 0.1 * GST_SECOND; + fail_unless (gst_pad_push (srcpad, buffer) == GST_FLOW_OK); + } + + decoder_srcpad = gst_element_get_static_pad (decoder, "src"); + + { + buffer = gst_buffer_new (); + GST_BUFFER_PTS (buffer) = 0.9 * GST_SECOND; + GST_BUFFER_DURATION (buffer) = 0.1 * GST_SECOND; + fail_unless (gst_pad_push (decoder_srcpad, buffer) == GST_FLOW_OK); + } + + reports = gst_validate_runner_get_reports (runner); + + assert_equals_int (g_list_length (reports), 1); + report = reports->data; + fail_unless_equals_int (report->level, GST_VALIDATE_REPORT_LEVEL_WARNING); + fail_unless_equals_int (report->issue->issue_id, + BUFFER_TIMESTAMP_OUT_OF_RECEIVED_RANGE); + g_list_free_full (reports, (GDestroyNotify) gst_validate_report_unref); + + gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL); + + gst_object_unref (decoder_srcpad); + gst_object_unref (srcpad); + + _stop_monitoring_bin (pipeline, runner); +} + +GST_END_TEST; + + +GST_START_TEST (flow_error_without_message) +{ + GstElement *decoder = fake_decoder_new (); + GstElement *src = gst_element_factory_make ("fakesrc", NULL); + GstElement *sink = gst_element_factory_make ("fakesink", NULL); + GstBin *pipeline = GST_BIN (gst_pipeline_new ("validate-pipeline")); + GList *reports; + GstValidateRunner *runner; + GstValidateReport *report; + + fail_unless (g_setenv ("GST_VALIDATE_REPORTING_DETAILS", "all", TRUE)); + runner = _start_monitoring_bin (pipeline); + + gst_bin_add_many (pipeline, src, decoder, sink, NULL); + fail_unless (gst_element_link_many (src, decoder, sink, NULL)); + + + FAKE_DECODER (decoder)->return_value = GST_FLOW_ERROR; + ASSERT_SET_STATE (GST_ELEMENT (pipeline), GST_STATE_PLAYING, + GST_STATE_CHANGE_ASYNC); + + gst_element_get_state (GST_ELEMENT (pipeline), NULL, NULL, + GST_CLOCK_TIME_NONE); + + reports = gst_validate_runner_get_reports (runner); + + fail_unless (g_list_length (reports) >= 1); + report = reports->data; + fail_unless_equals_int (report->level, GST_VALIDATE_REPORT_LEVEL_WARNING); + fail_unless_equals_int (report->issue->issue_id, + FLOW_ERROR_WITHOUT_ERROR_MESSAGE); + g_list_free_full (reports, (GDestroyNotify) gst_validate_report_unref); + + gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL); + + _stop_monitoring_bin (pipeline, runner); +} + +GST_END_TEST; + + +GST_START_TEST (flow_error_with_message) +{ + GstElement *decoder = fake_decoder_new (); + GstElement *src = gst_element_factory_make ("fakesrc", NULL); + GstElement *sink = gst_element_factory_make ("fakesink", NULL); + GstBin *pipeline = GST_BIN (gst_pipeline_new ("validate-pipeline")); + GList *reports; + GstValidateRunner *runner; + GstValidateReport *report; + const GError gerror = + { G_IO_ERROR, G_IO_ERROR_FAILED, (gchar *) "fake error" }; + + fail_unless (g_setenv ("GST_VALIDATE_REPORTING_DETAILS", "all", TRUE)); + runner = _start_monitoring_bin (pipeline); + + gst_bin_add_many (pipeline, src, decoder, sink, NULL); + fail_unless (gst_element_link_many (src, decoder, sink, NULL)); + + g_object_set (src, "is-live", TRUE, NULL); + + FAKE_DECODER (decoder)->return_value = GST_FLOW_ERROR; + + ASSERT_SET_STATE (GST_ELEMENT (pipeline), GST_STATE_PAUSED, + GST_STATE_CHANGE_NO_PREROLL); + + gst_element_post_message (decoder, + gst_message_new_error (GST_OBJECT (decoder), + (GError *) & gerror, "This is a fake error")); + + ASSERT_SET_STATE (GST_ELEMENT (pipeline), GST_STATE_PLAYING, + GST_STATE_CHANGE_ASYNC); + + gst_element_get_state (GST_ELEMENT (pipeline), NULL, NULL, + GST_CLOCK_TIME_NONE); + + reports = gst_validate_runner_get_reports (runner); + + assert_equals_int (g_list_length (reports), 1); + report = reports->data; + fail_unless_equals_int (report->issue->issue_id, ERROR_ON_BUS); + g_list_free_full (reports, (GDestroyNotify) gst_validate_report_unref); + + gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL); + + _stop_monitoring_bin (pipeline, runner); +} + +GST_END_TEST; + + + +static Suite * +gst_validate_suite (void) +{ + Suite *s = suite_create ("padmonitor"); + TCase *tc_chain = tcase_create ("padmonitor"); + suite_add_tcase (s, tc_chain); + + gst_validate_init (); + + tcase_add_test (tc_chain, buffer_before_segment); + tcase_add_test (tc_chain, buffer_outside_segment); + tcase_add_test (tc_chain, buffer_timestamp_out_of_received_range); + tcase_add_test (tc_chain, flow_aggregation); + tcase_add_test (tc_chain, issue_concatenation); + tcase_add_test (tc_chain, check_media_info); + tcase_add_test (tc_chain, eos_without_segment); + tcase_add_test (tc_chain, caps_events); + tcase_add_test (tc_chain, flow_error_without_message); + tcase_add_test (tc_chain, flow_error_with_message); + + gst_validate_deinit (); + return s; +} + +GST_CHECK_MAIN (gst_validate); diff --git a/validate/tests/check/validate/reporting.c b/validate/tests/check/validate/reporting.c new file mode 100644 index 0000000..7246b5f --- /dev/null +++ b/validate/tests/check/validate/reporting.c @@ -0,0 +1,315 @@ +/* GstValidate + * Copyright (C) 2014 Mathieu Duponchelle + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include "test-utils.h" + +GST_START_TEST (test_report_levels) +{ + GstValidateRunner *runner; + GstObject *pipeline; + GError *error = NULL; + GstElement *element; + GstValidateMonitor *monitor, *pipeline_monitor; + GstPad *pad; + + /* FIXME: for now the only interface to set the reporting level is through an + * environment variable parsed at the time of the runner initialization, + * we can simplify that if the runner exposes API for that at some point + */ + + /* Try to set the default reporting level to ALL, the code is supposed to + * be case insensitive */ + fail_unless (g_setenv ("GST_VALIDATE_REPORTING_DETAILS", "AlL", TRUE)); + runner = gst_validate_runner_new (); + fail_unless (gst_validate_runner_get_default_reporting_level (runner) == + GST_VALIDATE_SHOW_ALL); + g_object_unref (runner); + + /* Try to set the default reporting level to subchain, the code is supposed to + * parse numbers as well */ + fail_unless (g_setenv ("GST_VALIDATE_REPORTING_DETAILS", "2", TRUE)); + runner = gst_validate_runner_new (); + fail_unless (gst_validate_runner_get_default_reporting_level (runner) == + GST_VALIDATE_SHOW_SYNTHETIC); + g_object_unref (runner); + + /* Try to set the reporting level for an object */ + fail_unless (g_setenv ("GST_VALIDATE_REPORTING_DETAILS", + "synthetic,test_object:monitor,other_*:all", TRUE)); + runner = gst_validate_runner_new (); + fail_unless (gst_validate_runner_get_reporting_level_for_name (runner, + "test_object") == GST_VALIDATE_SHOW_MONITOR); + fail_unless (gst_validate_runner_get_reporting_level_for_name (runner, + "other_test_object") == GST_VALIDATE_SHOW_ALL); + fail_unless (gst_validate_runner_get_reporting_level_for_name (runner, + "dummy_test_object") == GST_VALIDATE_SHOW_UNKNOWN); + + g_object_unref (runner); + + /* Now let's try to see if the created monitors actually understand the + * situation they've put themselves into */ + fail_unless (g_setenv ("GST_VALIDATE_REPORTING_DETAILS", + "none,pipeline*:monitor,sofake1:all,sofake*::sink:subchain", TRUE)); + runner = gst_validate_runner_new (); + + pipeline = (GstObject *) + gst_parse_launch ("fakesrc name=sofake1 ! fakesink name=sofake2", &error); + fail_unless (pipeline != NULL); + pipeline_monitor = + gst_validate_monitor_factory_create (GST_OBJECT (pipeline), runner, NULL); + + element = gst_bin_get_by_name (GST_BIN (pipeline), "sofake1"); + monitor = + (GstValidateMonitor *) g_object_get_data (G_OBJECT (element), + "validate-monitor"); + fail_unless (gst_validate_reporter_get_reporting_level (GST_VALIDATE_REPORTER + (monitor)) == GST_VALIDATE_SHOW_ALL); + + pad = gst_element_get_static_pad (element, "src"); + monitor = + (GstValidateMonitor *) g_object_get_data (G_OBJECT (pad), + "validate-monitor"); + /* The pad should have inherited the reporting level */ + fail_unless (gst_validate_reporter_get_reporting_level (GST_VALIDATE_REPORTER + (monitor)) == GST_VALIDATE_SHOW_ALL); + gst_object_unref (pad); + + gst_object_unref (element); + + element = gst_bin_get_by_name (GST_BIN (pipeline), "sofake2"); + monitor = + (GstValidateMonitor *) g_object_get_data (G_OBJECT (element), + "validate-monitor"); + /* The element should have inherited its reporting level from the pipeline */ + fail_unless (gst_validate_reporter_get_reporting_level (GST_VALIDATE_REPORTER + (monitor)) == GST_VALIDATE_SHOW_MONITOR); + + pad = gst_element_get_static_pad (element, "sink"); + monitor = + (GstValidateMonitor *) g_object_get_data (G_OBJECT (pad), + "validate-monitor"); + /* But its pad should not as it falls in the sofake*::sink pattern */ + fail_unless (gst_validate_reporter_get_reporting_level (GST_VALIDATE_REPORTER + (monitor)) == GST_VALIDATE_SHOW_SUBCHAIN); + gst_object_unref (pad); + + gst_object_unref (element); + + g_object_unref (pipeline_monitor); + gst_object_unref (pipeline); + g_object_unref (runner); +} + +GST_END_TEST; + +static GstPadProbeReturn +drop_buffers (GstPad * pad, GstPadProbeInfo * info, gpointer unused) +{ + return GST_PAD_PROBE_DROP; +} + +static void +_create_issues (GstValidateRunner * runner) +{ + GstPad *srcpad1, *srcpad2, *sinkpad, *funnel_sink1, *funnel_sink2; + GstElement *src1, *src2, *sink, *funnel; + GstSegment segment; + gulong probe_id1, probe_id2; + + src1 = create_and_monitor_element ("fakesrc", "fakesrc1", runner); + src2 = create_and_monitor_element ("fakesrc", "fakesrc2", runner); + funnel = create_and_monitor_element ("funnel", "funnel", runner); + sink = create_and_monitor_element ("fakesink", "fakesink", runner); + + srcpad1 = gst_element_get_static_pad (src1, "src"); + srcpad2 = gst_element_get_static_pad (src2, "src"); + funnel_sink1 = gst_element_get_request_pad (funnel, "sink_%u"); + funnel_sink2 = gst_element_get_request_pad (funnel, "sink_%u"); + sinkpad = gst_element_get_static_pad (sink, "sink"); + + fail_unless (gst_element_link (funnel, sink)); + fail_unless (gst_pad_link (srcpad1, funnel_sink1) == GST_PAD_LINK_OK); + fail_unless (gst_pad_link (srcpad2, funnel_sink2) == GST_PAD_LINK_OK); + + /* There's gonna be some clunkiness in here because of funnel */ + probe_id1 = gst_pad_add_probe (srcpad1, + GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BUFFER_LIST | GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, + (GstPadProbeCallback) drop_buffers, NULL, NULL); + probe_id2 = gst_pad_add_probe (srcpad2, + GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BUFFER_LIST | GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, + (GstPadProbeCallback) drop_buffers, NULL, NULL); + + /* We want to handle the src behaviour ourselves */ + fail_unless (gst_pad_activate_mode (srcpad1, GST_PAD_MODE_PUSH, TRUE)); + fail_unless (gst_pad_activate_mode (srcpad2, GST_PAD_MODE_PUSH, TRUE)); + + /* Setup all needed events */ + gst_segment_init (&segment, GST_FORMAT_TIME); + segment.start = 0; + segment.stop = GST_SECOND; + + fail_unless (gst_pad_push_event (srcpad1, + gst_event_new_stream_start ("the-stream"))); + fail_unless (gst_pad_push_event (srcpad1, gst_event_new_segment (&segment))); + + fail_unless (gst_pad_push_event (srcpad2, + gst_event_new_stream_start ("the-stream"))); + fail_unless (gst_pad_push_event (srcpad2, gst_event_new_segment (&segment))); + + fail_unless_equals_int (gst_element_set_state (funnel, GST_STATE_PLAYING), + GST_STATE_CHANGE_SUCCESS); + fail_unless_equals_int (gst_element_set_state (sink, GST_STATE_PLAYING), + GST_STATE_CHANGE_ASYNC); + + /* Send an unexpected flush stop */ + _gst_check_expecting_log = TRUE; + fail_unless (gst_pad_push_event (srcpad1, gst_event_new_flush_stop (TRUE))); + + /* Once again but on the other funnel sink */ + fail_unless (gst_pad_push_event (srcpad2, gst_event_new_flush_stop (TRUE))); + + /* clean up */ + fail_unless (gst_pad_activate_mode (srcpad1, GST_PAD_MODE_PUSH, FALSE)); + fail_unless (gst_pad_activate_mode (srcpad2, GST_PAD_MODE_PUSH, FALSE)); + fail_unless_equals_int (gst_element_set_state (funnel, GST_STATE_NULL), + GST_STATE_CHANGE_SUCCESS); + fail_unless_equals_int (gst_element_set_state (sink, GST_STATE_NULL), + GST_STATE_CHANGE_SUCCESS); + + gst_pad_remove_probe (srcpad1, probe_id1); + gst_pad_remove_probe (srcpad2, probe_id2); + + gst_object_unref (srcpad1); + gst_object_unref (srcpad2); + gst_object_unref (sinkpad); + gst_object_unref (funnel_sink1); + gst_object_unref (funnel_sink2); + check_destroyed (funnel, funnel_sink1, funnel_sink2, NULL); + check_destroyed (src1, srcpad1, NULL); + check_destroyed (src2, srcpad2, NULL); + check_destroyed (sink, sinkpad, NULL); +} + +GST_START_TEST (test_global_levels) +{ + GstValidateRunner *runner; + + fail_unless (g_setenv ("GST_VALIDATE_REPORTING_DETAILS", "none", TRUE)); + runner = gst_validate_runner_new (); + _create_issues (runner); + /* None shall pass */ + fail_unless_equals_int (gst_validate_runner_get_reports_count (runner), 0); + g_object_unref (runner); + + fail_unless (g_setenv ("GST_VALIDATE_REPORTING_DETAILS", "synthetic", TRUE)); + runner = gst_validate_runner_new (); + _create_issues (runner); + /* Two reports of the same type */ + fail_unless_equals_int (gst_validate_runner_get_reports_count (runner), 1); + g_object_unref (runner); + + fail_unless (g_setenv ("GST_VALIDATE_REPORTING_DETAILS", "monitor", TRUE)); + runner = gst_validate_runner_new (); + _create_issues (runner); + /* One report for each pad monitor */ + fail_unless_equals_int (gst_validate_runner_get_reports_count (runner), 6); + g_object_unref (runner); + + fail_unless (g_setenv ("GST_VALIDATE_REPORTING_DETAILS", "all", TRUE)); + runner = gst_validate_runner_new (); + _create_issues (runner); + /* One report for each pad monitor, plus one for funnel src and fakesink sink */ + fail_unless_equals_int (gst_validate_runner_get_reports_count (runner), 8); + g_object_unref (runner); +} + +GST_END_TEST; + +GST_START_TEST (test_specific_levels) +{ + GstValidateRunner *runner; + + fail_unless (g_setenv ("GST_VALIDATE_REPORTING_DETAILS", "none,fakesrc1:synthetic", + TRUE)); + runner = gst_validate_runner_new (); + _create_issues (runner); + /* One issue should go through the none filter */ + fail_unless_equals_int (gst_validate_runner_get_reports_count (runner), 1); + g_object_unref (runner); + + fail_unless (g_setenv ("GST_VALIDATE_REPORTING_DETAILS", "monitor,sink:none", + TRUE)); + runner = gst_validate_runner_new (); + _create_issues (runner); + /* 5 issues because all pads will report their own issues separately, except + * for the sink which will not report an issue */ + fail_unless_equals_int (gst_validate_runner_get_reports_count (runner), 5); + g_object_unref (runner); + + fail_unless (g_setenv ("GST_VALIDATE_REPORTING_DETAILS", "subchain,sink:monitor", + TRUE)); + runner = gst_validate_runner_new (); + _create_issues (runner); + /* 3 issues because both fake sources will have subsequent subchains of + * issues, and the sink will report its issue separately */ + fail_unless_equals_int (gst_validate_runner_get_reports_count (runner), 3); + g_object_unref (runner); + + fail_unless (g_setenv ("GST_VALIDATE_REPORTING_DETAILS", + "synthetic,fakesrc1:subchain,fakesrc2:subchain,funnel*::src*:monitor", + TRUE)); + runner = gst_validate_runner_new (); + _create_issues (runner); + /* 4 issues because the funnel sink issues will be concatenated with the + * fakesrc issues, the funnel src will report its issue separately, and the + * sink will not find a report immediately upstream */ + fail_unless_equals_int (gst_validate_runner_get_reports_count (runner), 4); + g_object_unref (runner); + + fail_unless (g_setenv ("GST_VALIDATE_REPORTING_DETAILS", "none,fakesink*:all", + TRUE)); + runner = gst_validate_runner_new (); + _create_issues (runner); + /* 2 issues repeated on the fakesink's sink */ + fail_unless_equals_int (gst_validate_runner_get_reports_count (runner), 2); + g_object_unref (runner); +} + +GST_END_TEST; + +static Suite * +gst_validate_suite (void) +{ + Suite *s = suite_create ("reporting"); + TCase *tc_chain = tcase_create ("reporting"); + suite_add_tcase (s, tc_chain); + + gst_validate_init (); + + tcase_add_test (tc_chain, test_report_levels); + tcase_add_test (tc_chain, test_global_levels); + tcase_add_test (tc_chain, test_specific_levels); + + gst_validate_deinit (); + return s; +} + +GST_CHECK_MAIN (gst_validate); diff --git a/validate/tests/check/validate/test-utils.c b/validate/tests/check/validate/test-utils.c new file mode 100644 index 0000000..5159fdb --- /dev/null +++ b/validate/tests/check/validate/test-utils.c @@ -0,0 +1,301 @@ +/* GstValidate + * Copyright (C) 2014 Thibault Saunier + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "test-utils.h" + +typedef struct _DestroyedObjectStruct +{ + GObject *object; + gboolean destroyed; +} DestroyedObjectStruct; + +static void +weak_notify (DestroyedObjectStruct * destroyed, GObject ** object) +{ + destroyed->destroyed = TRUE; +} + +void +check_destroyed (gpointer object_to_unref, gpointer first_object, ...) +{ + gint i = 0; + GObject *object; + GList *objs = NULL, *tmp; + DestroyedObjectStruct *destroyed = g_slice_new0 (DestroyedObjectStruct); + + destroyed->object = G_OBJECT (object_to_unref); + g_object_weak_ref (G_OBJECT (object_to_unref), (GWeakNotify) weak_notify, + destroyed); + objs = g_list_prepend (objs, destroyed); + + if (first_object) { + va_list varargs; + + object = G_OBJECT (first_object); + + va_start (varargs, first_object); + while (object) { + destroyed = g_slice_new0 (DestroyedObjectStruct); + destroyed->object = object; + g_object_weak_ref (object, (GWeakNotify) weak_notify, destroyed); + objs = g_list_append (objs, destroyed); + object = va_arg (varargs, GObject *); + } + va_end (varargs); + } + gst_object_unref (object_to_unref); + + for (tmp = objs; tmp; tmp = tmp->next) { + fail_unless (((DestroyedObjectStruct *) tmp->data)->destroyed == TRUE, + "%p is not destroyed (object nb %i)", + ((DestroyedObjectStruct *) tmp->data)->object, i); + g_slice_free (DestroyedObjectStruct, tmp->data); + i++; + } + g_list_free (objs); + +} + +void +clean_bus (GstElement * element) +{ + GstBus *bus; + + bus = gst_element_get_bus (element); + gst_bus_set_flushing (bus, TRUE); + gst_object_unref (bus); +} + +GstValidatePadMonitor * +get_pad_monitor (GstPad * pad) +{ + return g_object_get_data ((GObject *) pad, "validate-monitor"); +} + +static GstStaticPadTemplate fake_demuxer_src_template = +GST_STATIC_PAD_TEMPLATE ("src%u", + GST_PAD_SRC, + GST_PAD_SOMETIMES, + GST_STATIC_CAPS ("something") + ); + +static GstStaticPadTemplate fake_demuxer_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("something") + ); + +static GstFlowReturn +_demuxer_chain (GstPad * pad, GstObject * self, GstBuffer * buffer) +{ + gst_buffer_unref (buffer); + + return FAKE_DEMUXER (self)->return_value; +} + +static void +fake_demuxer_init (FakeDemuxer * self, gpointer * g_class) +{ + GstPad *pad; + GstElement *element = GST_ELEMENT (self); + GstPadTemplate *pad_template; + + pad_template = + gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src%u"); + + pad = gst_pad_new_from_template (pad_template, "src0"); + gst_element_add_pad (element, pad); + + pad = gst_pad_new_from_template (pad_template, "src1"); + gst_element_add_pad (element, pad); + + pad = gst_pad_new_from_template (pad_template, "src2"); + gst_element_add_pad (element, pad); + + pad_template = + gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "sink"); + pad = gst_pad_new_from_template (pad_template, "sink"); + gst_element_add_pad (element, pad); + + self->return_value = GST_FLOW_OK; + + gst_pad_set_chain_function (pad, _demuxer_chain); +} + +static void +fake_demuxer_class_init (FakeDemuxerClass * self_class) +{ + GstElementClass *gstelement_class = GST_ELEMENT_CLASS (self_class); + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&fake_demuxer_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&fake_demuxer_sink_template)); + gst_element_class_set_static_metadata (gstelement_class, + "Fake Demuxer", "Demuxer", "Some demuxer", "Thibault Saunier"); +} + +GType +fake_demuxer_get_type (void) +{ + static volatile gsize type = 0; + + if (g_once_init_enter (&type)) { + GType _type; + static const GTypeInfo info = { + sizeof (FakeDemuxerClass), + NULL, + NULL, + (GClassInitFunc) fake_demuxer_class_init, + NULL, + NULL, + sizeof (FakeDemuxer), + 0, + (GInstanceInitFunc) fake_demuxer_init, + }; + + _type = g_type_register_static (GST_TYPE_ELEMENT, "FakeDemuxer", &info, 0); + g_once_init_leave (&type, _type); + } + return type; +} + +GstElement * +fake_demuxer_new (void) +{ + return GST_ELEMENT (g_object_new (FAKE_DEMUXER_TYPE, NULL)); +} + +GstElement * +create_and_monitor_element (const gchar * factoryname, const gchar * name, + GstValidateRunner * runner) +{ + GstElement *element; + GstValidateMonitor *monitor; + + element = gst_element_factory_make (factoryname, name); + if (runner) { + monitor = + gst_validate_monitor_factory_create (GST_OBJECT (element), runner, + NULL); + fail_unless (GST_IS_VALIDATE_ELEMENT_MONITOR (monitor)); + } + + return element; +} + +void +free_element_monitor (GstElement * element) +{ + GstValidateMonitor *monitor; + monitor = + (GstValidateMonitor *) g_object_get_data (G_OBJECT (element), + "validate-monitor"); + + g_object_unref (G_OBJECT (monitor)); +} + +static GstStaticPadTemplate fake_decoder_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_SOMETIMES, + GST_STATIC_CAPS ("video/x-raw") + ); + +static GstStaticPadTemplate fake_decoder_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-raw") + ); + +static GstFlowReturn +_decoder_chain (GstPad * pad, GstObject * self, GstBuffer * buffer) +{ + gst_buffer_unref (buffer); + + return FAKE_DECODER (self)->return_value; +} + +static void +fake_decoder_init (FakeDecoder * self, gpointer * g_class) +{ + GstPad *pad; + GstElement *element = GST_ELEMENT (self); + GstPadTemplate *pad_template; + + pad_template = + gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src"); + pad = gst_pad_new_from_template (pad_template, "src"); + gst_element_add_pad (element, pad); + + pad_template = + gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "sink"); + pad = gst_pad_new_from_template (pad_template, "sink"); + gst_element_add_pad (element, pad); + + self->return_value = GST_FLOW_OK; + + gst_pad_set_chain_function (pad, _decoder_chain); +} + +static void +fake_decoder_class_init (FakeDecoderClass * self_class) +{ + GstElementClass *gstelement_class = GST_ELEMENT_CLASS (self_class); + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&fake_decoder_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&fake_decoder_sink_template)); + gst_element_class_set_static_metadata (gstelement_class, + "Fake Decoder", "Decoder", "Some decoder", "Thibault Saunier"); +} + +GType +fake_decoder_get_type (void) +{ + static volatile gsize type = 0; + + if (g_once_init_enter (&type)) { + GType _type; + static const GTypeInfo info = { + sizeof (FakeDecoderClass), + NULL, + NULL, + (GClassInitFunc) fake_decoder_class_init, + NULL, + NULL, + sizeof (FakeDecoder), + 0, + (GInstanceInitFunc) fake_decoder_init, + }; + + _type = g_type_register_static (GST_TYPE_ELEMENT, "FakeDecoder", &info, 0); + g_once_init_leave (&type, _type); + } + return type; +} + +GstElement * +fake_decoder_new (void) +{ + return GST_ELEMENT (g_object_new (FAKE_DECODER_TYPE, NULL)); +} diff --git a/validate/tests/check/validate/test-utils.h b/validate/tests/check/validate/test-utils.h new file mode 100644 index 0000000..1854048 --- /dev/null +++ b/validate/tests/check/validate/test-utils.h @@ -0,0 +1,79 @@ +/* GstValidate + * Copyright (C) 2014 Thibault Saunier + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _GST_VALIDATE_TEST_UTILS +#define _GST_VALIDATE_TEST_UTILS + +#include "test-utils.h" +#include +#include +#include + +G_BEGIN_DECLS + +void check_destroyed (gpointer object_to_unref, gpointer first_object, ...) G_GNUC_NULL_TERMINATED; +GstValidateRunner * setup_runner (GstObject * object); +void clean_bus (GstElement *element); +GstValidatePadMonitor * get_pad_monitor (GstPad *pad); +GstElement * create_and_monitor_element (const gchar *factoryname, const gchar *name, GstValidateRunner *runner); +void free_element_monitor (GstElement *element); + +typedef struct { + GstElement parent; + + GstFlowReturn return_value; +} FakeDemuxer; + +typedef struct { + GstElementClass parent; +} FakeDemuxerClass; + +#define FAKE_DEMUXER_TYPE (fake_demuxer_get_type ()) +#define FAKE_DEMUXER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FAKE_DEMUXER_TYPE, FakeDemuxer)) +#define FAKE_DEMUXER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FAKE_DEMUXER_TYPE, FakeDemuxerClass)) +#define IS_FAKE_DEMUXER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FAKE_DEMUXER_TYPE)) +#define IS_FAKE_DEMUXER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FAKE_DEMUXER_TYPE)) +#define FAKE_DEMUXER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), FAKE_DEMUXER_TYPE, FakeDemuxerClass)) + +GType fake_demuxer_get_type (void); +GstElement * fake_demuxer_new (void); + +typedef struct { + GstElement parent; + + GstFlowReturn return_value; +} FakeDecoder; + +typedef struct { + GstElementClass parent; +} FakeDecoderClass; + +#define FAKE_DECODER_TYPE (fake_decoder_get_type ()) +#define FAKE_DECODER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FAKE_DECODER_TYPE, FakeDecoder)) +#define FAKE_DECODER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FAKE_DECODER_TYPE, FakeDecoderClass)) +#define IS_FAKE_DECODER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FAKE_DECODER_TYPE)) +#define IS_FAKE_DECODER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FAKE_DECODER_TYPE)) +#define FAKE_DECODER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), FAKE_DECODER_TYPE, FakeDecoderClass)) + +GType fake_decoder_get_type (void); +GstElement * fake_decoder_new (void); + +G_END_DECLS + +#endif /* _GST_VALIDATE_TEST_UTILS */ diff --git a/validate/tools/.gitignore b/validate/tools/.gitignore new file mode 100644 index 0000000..6c0414b --- /dev/null +++ b/validate/tools/.gitignore @@ -0,0 +1,8 @@ +gst-validate-1.0 +gst-validate-transcoding-1.0 +gst-validate-media-check-1.0 +gst-validate-images-check-1.0 +gst-validate-1.0-debug +gst-validate-media-check-1.0-debug +gst-validate-transcoding-1.0-debug +gst-validate-images-check-1.0-debug diff --git a/validate/tools/Makefile.am b/validate/tools/Makefile.am new file mode 100644 index 0000000..236ab2b --- /dev/null +++ b/validate/tools/Makefile.am @@ -0,0 +1,48 @@ +bin_PROGRAMS = \ + gst-validate-@GST_API_VERSION@ \ + gst-validate-transcoding-@GST_API_VERSION@ \ + gst-validate-media-check-@GST_API_VERSION@ + +noinst_PROGRAMS = \ + gst-validate-@GST_API_VERSION@-debug \ + gst-validate-transcoding-@GST_API_VERSION@-debug \ + gst-validate-media-check-@GST_API_VERSION@-debug + +bin_SCRIPTS = \ + gst-validate-launcher + +AM_CFLAGS = $(GST_ALL_CFLAGS) $(GST_PBUTILS_CFLAGS) $(GST_VIDEO_CFLAGS) +LDADD = $(top_builddir)/gst/validate/libgstvalidate-@GST_API_VERSION@.la $(GST_PBUTILS_LIBS) $(GST_LIBS) $(GST_VIDEO_LIBS) + +# The -debug versions are used when running from sources to not confuse +# valgrind with libtool's wrappers. Those are built with the '-no-install' and +# so use rpath instead of wrappers. +gst_validate_@GST_API_VERSION@_SOURCES = gst-validate.c +gst_validate_@GST_API_VERSION@_CFLAGS = $(GIO_CFLAGS) $(AM_CFLAGS) +gst_validate_@GST_API_VERSION@_LDADD = $(GIO_LIBS) $(LDADD) + +gst_validate_@GST_API_VERSION@_debug_SOURCES = gst-validate.c +gst_validate_@GST_API_VERSION@_debug_CFLAGS = $(GIO_CFLAGS) $(AM_CFLAGS) +gst_validate_@GST_API_VERSION@_debug_LDADD = $(GIO_LIBS) $(LDADD) +gst_validate_@GST_API_VERSION@_debug_LDFLAGS = -no-install + +gst_validate_transcoding_@GST_API_VERSION@_SOURCES = gst-validate-transcoding.c +gst_validate_transcoding_@GST_API_VERSION@_debug_SOURCES = gst-validate-transcoding.c +gst_validate_transcoding_@GST_API_VERSION@_debug_LDFLAGS = -no-install + +gst_validate_media_check_@GST_API_VERSION@_SOURCES = gst-validate-media-check.c +gst_validate_media_check_@GST_API_VERSION@_debug_SOURCES = gst-validate-media-check.c +gst_validate_media_check_@GST_API_VERSION@_debug_LDFLAGS = -no-install + +if HAVE_CAIRO +bin_PROGRAMS += gst-validate-images-check-@GST_API_VERSION@ +noinst_PROGRAMS += gst-validate-images-check-@GST_API_VERSION@-debug + +gst_validate_images_check_@GST_API_VERSION@_SOURCES = gst-validate-images-check.c +gst_validate_images_check_@GST_API_VERSION@_LDADD = $(top_builddir)/gst-libs/gst/video/libgstvalidatevideo-@GST_API_VERSION@.la $(LDADD) +gst_validate_images_check_@GST_API_VERSION@_debug_SOURCES = gst-validate-images-check.c +gst_validate_images_check_@GST_API_VERSION@_debug_LDADD = $(top_builddir)/gst-libs/gst/video/libgstvalidatevideo-@GST_API_VERSION@.la $(LDADD) +gst_validate_images_check_@GST_API_VERSION@_debug_LDFLAGS = -no-install +endif + +CLEANFILES = $(bin_SCRIPTS) diff --git a/validate/tools/gst-validate-images-check.c b/validate/tools/gst-validate-images-check.c new file mode 100644 index 0000000..b4b2fbd --- /dev/null +++ b/validate/tools/gst-validate-images-check.c @@ -0,0 +1,114 @@ +/* GStreamer + * + * Copyright (C) 2014 Mathieu Duponchelle + * Copyright (C) 2015 Raspberry Pi Foundation + * Author: Thibault Saunier + * + * This library 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.1 of the License, or (at your option) any later version. + * + * This library 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; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "../gst-libs/gst/video/gstvalidatessim.h" + +#include +#include +#include + +int +main (int argc, char **argv) +{ + GstValidateSsim *ssim; + gint rep_err, ret = 0; + GError *err = NULL; + GstValidateRunner *runner = NULL; + GOptionContext *ctx; + const gchar *outfolder = NULL; + gfloat mssim = 0, lowest = 1, highest = -1; + gdouble min_avg_similarity = 0.95, min_lowest_similarity = -1.0; + + GOptionEntry options[] = { + {"min-avg-similarity", 'a', 0, G_OPTION_ARG_DOUBLE, + &min_avg_similarity, + "The minimum average similarity under which we consider" + "the test as failing", + NULL}, + {"min-lowest-similarity", 'l', 0, G_OPTION_ARG_DOUBLE, + &min_lowest_similarity, + "The minimum 'lowest' similarity under which we consider" + "the test as failing", + NULL}, + {"result-output-folder", 'r', 0, G_OPTION_ARG_STRING, + &outfolder, + "The folder in which to store resulting grey scale images" + " when the test failed. In that folder you will find" + " images with the structural difference between" + " the reference frame and the failed one", + NULL}, + {NULL} + }; + + g_set_prgname ("gst-validate-mages-check-" GST_API_VERSION); + ctx = g_option_context_new ("/reference/file/path /compared/file/path"); + + g_option_context_set_summary (ctx, + "The gst-validate-images-check calculates SSIM (Structural SIMilarity) " + " index for the images. And according to min-lowest-similarity and" + " min-avg-similarity, it will concider the images similare enough" + " or report critical issues in the GstValidate reporting system"); + g_option_context_add_main_entries (ctx, options, NULL); + + if (!g_option_context_parse (ctx, &argc, &argv, &err)) { + g_printerr ("Error initializing: %s\n", err->message); + g_option_context_free (ctx); + return -1; + } + + if (argc != 3) { + gchar *help = g_option_context_get_help (ctx, FALSE, NULL); + g_printerr ("%s", help); + g_free (help); + g_option_context_free (ctx); + + return -1; + } + + gst_init (&argc, &argv); + gst_validate_init (); + + runner = gst_validate_runner_new (); + ssim = + gst_validate_ssim_new (runner, min_avg_similarity, min_lowest_similarity); + + gst_validate_ssim_compare_image_files (ssim, argv[1], argv[2], &mssim, + &lowest, &highest, outfolder); + + if (!g_file_test (argv[1], G_FILE_TEST_IS_DIR)) { + gst_validate_printf (ssim, "Compared %s with %s, average: %f, Min %f\n", + argv[1], argv[2], mssim, lowest); + } + + rep_err = gst_validate_runner_exit (runner, TRUE); + if (ret == 0) { + ret = rep_err; + if (rep_err != 0) + g_print ("Returning %d as error where found", rep_err); + } + + g_object_unref (ssim); + g_object_unref (runner); + gst_validate_deinit (); + + return ret; +} diff --git a/validate/tools/gst-validate-launcher.in b/validate/tools/gst-validate-launcher.in new file mode 100644 index 0000000..e165f3a --- /dev/null +++ b/validate/tools/gst-validate-launcher.in @@ -0,0 +1,62 @@ +#!/usr/bin/env python2 +# +# Copyright (c) 2014,Thibault Saunier +# +# This program 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.1 of the License, or (at your option) any later version. +# +# This program 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 program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301, USA. + +import os +import subprocess +import sys + +LIBDIR = '@LIBDIR@' +GIT_FIRST_HASH = 'da962d096af9460502843e41b7d25fdece7ff1c2' + + +def _get_git_first_hash(path): + cdir = os.path.abspath(os.curdir) + try: + os.chdir(path) + res = subprocess.check_output(['git', 'rev-list', '--max-parents=0', 'HEAD']).rstrip('\n') + except (subprocess.CalledProcessError, OSError): + res = '' + finally: + os.chdir(cdir) + + return res + + +def _in_devel(): + root_dir = os.path.abspath(os.path.dirname(os.path.join(os.path.dirname(os.path.abspath(__file__)), + "..", "..", ".."))) + return _get_git_first_hash(root_dir) == GIT_FIRST_HASH + + +def _add_gst_launcher_path(): + if not _in_devel(): + root = os.path.join(LIBDIR, 'gst-validate-launcher', 'python') + else: + print "Running with development path" + dir_ = os.path.dirname(os.path.abspath(__file__)) + root = os.path.split(dir_)[0] + + sys.path.insert(0, root) + return os.path.join(root, "launcher") + + +if "__main__" == __name__: + libsdir = _add_gst_launcher_path() + from launcher.main import main + exit(main(libsdir)) diff --git a/validate/tools/gst-validate-media-check.c b/validate/tools/gst-validate-media-check.c new file mode 100644 index 0000000..2b4f6de --- /dev/null +++ b/validate/tools/gst-validate-media-check.c @@ -0,0 +1,147 @@ +/* GStreamer + * + * Copyright (C) 2013 Collabora Ltd. + * Author: Thiago Sousa Santos + * + * gst-validate-media-check.c - Media Check CLI tool + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include + +int +main (int argc, gchar ** argv) +{ + GOptionContext *ctx; + + guint ret = 0; + GError *err = NULL; + gboolean full = FALSE; + gchar *output_file = NULL; + gchar *expected_file = NULL; + gchar *output = NULL; + GstMediaDescriptorWriter *writer = NULL; + GstValidateRunner *runner = NULL; + GstMediaDescriptorParser *reference = NULL; + + GOptionEntry options[] = { + {"output-file", 'o', 0, G_OPTION_ARG_FILENAME, + &output_file, "The output file to store the results", + NULL}, + {"full", 'f', 0, G_OPTION_ARG_NONE, + &full, "Fully analyze the file frame by frame", + NULL}, + {"expected-results", 'e', 0, G_OPTION_ARG_FILENAME, + &expected_file, "Path to file containing the expected results " + "(or the last results found) for comparison with new results", + NULL}, + {NULL} + }; + + g_set_prgname ("gst-validate-media-check-" GST_API_VERSION); + ctx = g_option_context_new ("[URI]"); + g_option_context_set_summary (ctx, "Analyzes a media file and writes " + "the results to stdout or a file. Can also compare the results found " + "with another results file for identifying regressions. The monitoring" + " lib from gst-validate will be enabled during the tests to identify " + "issues with the gstreamer elements involved with the media file's " + "container and codec types"); + g_option_context_add_main_entries (ctx, options, NULL); + + if (!g_option_context_parse (ctx, &argc, &argv, &err)) { + g_printerr ("Error initializing: %s\n", err->message); + g_option_context_free (ctx); + exit (1); + } + + gst_init (&argc, &argv); + gst_validate_init (); + + if (argc != 2) { + gchar *msg = g_option_context_get_help (ctx, TRUE, NULL); + g_printerr ("%s\n", msg); + g_free (msg); + g_option_context_free (ctx); + ret = 1; + goto out; + } + g_option_context_free (ctx); + + runner = gst_validate_runner_new (); + writer = + gst_media_descriptor_writer_new_discover (runner, argv[1], full, TRUE, + &err); + if (writer == NULL) { + g_print ("Could not discover file: %s\n", argv[1]); + ret = 1; + goto out; + } + + if (output_file) + gst_media_descriptor_writer_write (writer, output_file); + + if (expected_file) { + reference = gst_media_descriptor_parser_new (runner, expected_file, &err); + + if (reference == NULL) { + g_print ("Could not parse file: %s\n", expected_file); + ret = 1; + goto out; + } + + gst_media_descriptors_compare (GST_MEDIA_DESCRIPTOR (reference), + GST_MEDIA_DESCRIPTOR (writer)); + } else { + output = gst_media_descriptor_writer_serialize (writer); + g_print ("Media info:\n%s\n", output); + g_free (output); + } + + ret = gst_validate_runner_exit (runner, TRUE); + if (ret && expected_file) { + output = gst_media_descriptor_writer_serialize (writer); + g_print ("Media info:\n%s\n", output); + g_free (output); + } + +out: + g_free (output_file); + g_free (expected_file); + + if (reference) + gst_object_unref (reference); + if (writer) + gst_object_unref (writer); + if (runner) + gst_object_unref (runner); + gst_deinit (); + gst_validate_deinit (); + + return ret; +} diff --git a/validate/tools/gst-validate-transcoding.c b/validate/tools/gst-validate-transcoding.c new file mode 100644 index 0000000..04f5895 --- /dev/null +++ b/validate/tools/gst-validate-transcoding.c @@ -0,0 +1,966 @@ +/* GStreamer + * + * Copyright (C) 2013 Collabora Ltd. + * Author: Thibault Saunier + * + * gst-validate-transcoding.c - CLI tool to validate transcoding operations + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include +#include +#include +#include + + +#ifdef G_OS_UNIX +#include +#endif + +#include + +static gint ret = 0; +static GMainLoop *mainloop; +static GstElement *pipeline, *encodebin; +static GstEncodingProfile *encoding_profile = NULL; +static gboolean eos_on_shutdown = FALSE; +static gboolean force_reencoding = FALSE; +static GList *all_raw_caps = NULL; + +static gboolean buffering = FALSE; +static gboolean is_live = FALSE; + +typedef struct +{ + volatile gint refcount; + + GstSegment segment; /* The currently configured segment */ + + /* FIXME Do we need a weak ref here? */ + GstValidateScenario *scenario; + guint count_bufs; + gboolean seen_event; + GstClockTime running_time; + + /* Make sure to remove all probes when we are done */ + gboolean done; + +} KeyUnitProbeInfo; + +/* This is used to + * 1) Make sure we receive the event + * 2) Count the number of frames that were not KF seen after the event + */ +#define FORCE_KF_DATA_NAME "force-key-unit" +#define NOT_KF_AFTER_FORCE_KF_EVT_TOLERANCE 1 + +#ifdef G_OS_UNIX +static gboolean +intr_handler (gpointer user_data) +{ + g_print ("interrupt received.\n"); + + if (eos_on_shutdown) { + g_print ("Sending EOS to the pipeline\n"); + eos_on_shutdown = FALSE; + gst_element_send_event (GST_ELEMENT_CAST (user_data), gst_event_new_eos ()); + return TRUE; + } + g_main_loop_quit (mainloop); + + /* remove signal handler */ + return FALSE; +} +#endif /* G_OS_UNIX */ + +static void +key_unit_data_unref (KeyUnitProbeInfo * info) +{ + if (G_UNLIKELY (g_atomic_int_dec_and_test (&info->refcount))) { + g_slice_free (KeyUnitProbeInfo, info); + } +} + +static KeyUnitProbeInfo * +key_unit_data_ref (KeyUnitProbeInfo * info) +{ + g_atomic_int_inc (&info->refcount); + + return info; +} + +static KeyUnitProbeInfo * +key_unit_data_new (GstValidateScenario * scenario, GstClockTime running_time) +{ + KeyUnitProbeInfo *info = g_slice_new0 (KeyUnitProbeInfo); + info->refcount = 1; + + info->scenario = scenario; + info->running_time = running_time; + + return info; +} + +static GstPadProbeReturn +_check_is_key_unit_cb (GstPad * pad, GstPadProbeInfo * info, + KeyUnitProbeInfo * kuinfo) +{ + if (GST_IS_EVENT (GST_PAD_PROBE_INFO_DATA (info))) { + if (gst_video_event_is_force_key_unit (GST_PAD_PROBE_INFO_DATA (info))) + kuinfo->seen_event = TRUE; + else if (GST_EVENT_TYPE (info->data) == GST_EVENT_SEGMENT && + GST_PAD_DIRECTION (pad) == GST_PAD_SRC) { + const GstSegment *segment = NULL; + + gst_event_parse_segment (info->data, &segment); + kuinfo->segment = *segment; + } + } else if (GST_IS_BUFFER (GST_PAD_PROBE_INFO_DATA (info)) + && kuinfo->seen_event) { + + if (GST_CLOCK_TIME_IS_VALID (kuinfo->running_time)) { + GstClockTime running_time = gst_segment_to_running_time (&kuinfo->segment, + GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (info->data)); + + if (running_time < kuinfo->running_time) + return GST_PAD_PROBE_OK; + } + + if (GST_BUFFER_FLAG_IS_SET (GST_PAD_PROBE_INFO_BUFFER (info), + GST_BUFFER_FLAG_DELTA_UNIT)) { + if (kuinfo->count_bufs >= NOT_KF_AFTER_FORCE_KF_EVT_TOLERANCE) { + GST_VALIDATE_REPORT (kuinfo->scenario, + SCENARIO_ACTION_EXECUTION_ERROR, + "Did not receive a key frame after requested one, " + " at running_time %" GST_TIME_FORMAT " (with a %i " + "frame tolerance)", GST_TIME_ARGS (kuinfo->running_time), + NOT_KF_AFTER_FORCE_KF_EVT_TOLERANCE); + + return GST_PAD_PROBE_REMOVE; + } + + kuinfo->count_bufs++; + } else { + GST_DEBUG_OBJECT (kuinfo->scenario, + "Properly got keyframe after \"force-keyframe\" event " + "with running_time %" GST_TIME_FORMAT " (latency %d frame(s))", + GST_TIME_ARGS (kuinfo->running_time), kuinfo->count_bufs); + + return GST_PAD_PROBE_REMOVE; + } + } + + return GST_PAD_PROBE_OK; +} + +static int +_find_video_encoder (GValue * velement, gpointer udata) +{ + GstElement *element = g_value_get_object (velement); + + const gchar *klass = + gst_element_class_get_metadata (GST_ELEMENT_GET_CLASS (element), + GST_ELEMENT_METADATA_KLASS); + + if (g_strstr_len (klass, -1, "Video") && g_strstr_len (klass, -1, "Encoder")) { + + return 0; + } + + return !0; +} + + +static gboolean +_execute_request_key_unit (GstValidateScenario * scenario, + GstValidateAction * action) +{ + guint count; + GstIterator *iter; + gboolean all_headers; + + gboolean ret = TRUE; + GValue result = { 0, }; + GstEvent *event = NULL; + GstQuery *segment_query; + KeyUnitProbeInfo *info = NULL; + GstElement *video_encoder = NULL; + GstPad *pad = NULL, *encoder_srcpad = NULL; + GstClockTime running_time = GST_CLOCK_TIME_NONE; + const gchar *direction = gst_structure_get_string (action->structure, + "direction"); + + iter = gst_bin_iterate_recurse (GST_BIN (encodebin)); + if (!gst_iterator_find_custom (iter, + (GCompareFunc) _find_video_encoder, &result, NULL)) { + g_error ("Could not find any video encode"); + + goto fail; + } + + gst_iterator_free (iter); + video_encoder = g_value_get_object (&result); + encoder_srcpad = gst_element_get_static_pad (video_encoder, "src"); + + if (!encoder_srcpad) { + GST_FIXME ("Implement weird encoder management"); + g_error ("We do not handle encoder with not static srcpad"); + + goto fail; + } + + gst_validate_action_get_clocktime (scenario, action, + "running-time", &running_time); + + if (gst_structure_get_boolean (action->structure, "all-headers", + &all_headers)) { + g_error ("Missing field: all-headers"); + + goto fail; + } + + if (!gst_structure_get_uint (action->structure, "count", &count)) { + if (!gst_structure_get_int (action->structure, "count", (gint *) & count)) { + g_error ("Missing field: count"); + + goto fail; + } + } + + info = key_unit_data_new (scenario, running_time); + if (g_strcmp0 (direction, "upstream") == 0) { + event = gst_video_event_new_upstream_force_key_unit (running_time, + all_headers, count); + + pad = gst_element_get_static_pad (video_encoder, "src"); + gst_pad_add_probe (encoder_srcpad, + GST_PAD_PROBE_TYPE_EVENT_UPSTREAM, + (GstPadProbeCallback) _check_is_key_unit_cb, + key_unit_data_ref (info), (GDestroyNotify) key_unit_data_unref); + } else if (g_strcmp0 (direction, "downstream") == 0) { + GstClockTime timestamp = GST_CLOCK_TIME_NONE, + stream_time = GST_CLOCK_TIME_NONE; + + pad = gst_element_get_static_pad (video_encoder, "sink"); + if (!pad) { + GST_FIXME ("Implement weird encoder management"); + g_error ("We do not handle encoder with not static sinkpad"); + + goto fail; + } + + gst_validate_action_get_clocktime (scenario, action, + "timestamp", ×tamp); + + gst_validate_action_get_clocktime (scenario, action, + "stream-time", &stream_time); + + event = + gst_video_event_new_downstream_force_key_unit (timestamp, stream_time, + running_time, all_headers, count); + + gst_pad_add_probe (pad, + GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, + (GstPadProbeCallback) _check_is_key_unit_cb, + key_unit_data_ref (info), (GDestroyNotify) key_unit_data_unref); + } else { + g_error ("request keyunit direction %s invalide (should be in" + " [downstrean, upstream]", direction); + + goto fail; + } + + gst_validate_printf (action, "Sendings a \"force key unit\" event %s\n", + direction); + + segment_query = gst_query_new_segment (GST_FORMAT_TIME); + gst_pad_query (encoder_srcpad, segment_query); + + gst_query_parse_segment (segment_query, &(info->segment.rate), + &(info->segment.format), + (gint64 *) & (info->segment.start), (gint64 *) & (info->segment.stop)); + + gst_pad_add_probe (encoder_srcpad, + GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, + (GstPadProbeCallback) _check_is_key_unit_cb, info, + (GDestroyNotify) key_unit_data_unref); + + + if (!gst_pad_send_event (pad, event)) { + GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR, + "Could not send \"force key unit\" event %s", direction); + goto fail; + } + +done: + if (video_encoder) + gst_object_unref (video_encoder); + if (pad) + gst_object_unref (pad); + if (encoder_srcpad) + gst_object_unref (encoder_srcpad); + + return ret; + +fail: + ret = FALSE; + goto done; +} + +static gboolean +_execute_set_restriction (GstValidateScenario * scenario, + GstValidateAction * action) +{ + GstCaps *caps; + GType profile_type = G_TYPE_NONE; + const gchar *restriction_caps, *profile_type_name, *profile_name; + + restriction_caps = + gst_structure_get_string (action->structure, "restriction-caps"); + profile_type_name = + gst_structure_get_string (action->structure, "profile-type"); + profile_name = gst_structure_get_string (action->structure, "profile-name"); + + if (profile_type_name) { + profile_type = g_type_from_name (profile_type_name); + + if (profile_type == G_TYPE_NONE) { + g_error ("Profile name %s not known", profile_name); + + return FALSE; + } else if (profile_type == GST_TYPE_ENCODING_CONTAINER_PROFILE) { + g_error ("Can not set restrictions on container profiles"); + + return FALSE; + } + } else if (profile_name == NULL) { + if (g_strrstr (restriction_caps, "audio/x-raw") == restriction_caps) + profile_type = GST_TYPE_ENCODING_AUDIO_PROFILE; + else if (g_strrstr (restriction_caps, "video/x-raw") == restriction_caps) + profile_type = GST_TYPE_ENCODING_VIDEO_PROFILE; + else { + g_error + ("No information on what profiles to apply action, you should set either" + "profile_name or profile_type_name and the caps %s give us no hint", + restriction_caps); + + return FALSE; + } + } + + caps = gst_caps_from_string (restriction_caps); + if (caps == NULL) { + g_error ("Could not parse caps: %s", restriction_caps); + + return FALSE; + } + + if (GST_IS_ENCODING_CONTAINER_PROFILE (encoding_profile)) { + gboolean found = FALSE; + const GList *tmp; + + for (tmp = + gst_encoding_container_profile_get_profiles + (GST_ENCODING_CONTAINER_PROFILE (encoding_profile)); tmp; + tmp = tmp->next) { + GstEncodingProfile *profile = tmp->data; + + if (profile_type != G_TYPE_NONE + && G_OBJECT_TYPE (profile) == profile_type) { + gst_encoding_profile_set_restriction (profile, gst_caps_copy (caps)); + found = TRUE; + } else if (profile_name + && g_strcmp0 (gst_encoding_profile_get_name (profile), + profile_name) == 0) { + gst_encoding_profile_set_restriction (profile, gst_caps_copy (caps)); + found = TRUE; + } + } + + if (!found) { + g_error ("Could not find profile for %s%s", + profile_type_name ? profile_type_name : "", + profile_name ? profile_name : ""); + + gst_caps_unref (caps); + return FALSE; + + } + } + + if (profile_type != G_TYPE_NONE) { + gst_validate_printf (action, + "setting caps to %s on profiles of type %s\n", + restriction_caps, g_type_name (profile_type)); + } else { + gst_validate_printf (action, "setting caps to %s on profile %s\n", + restriction_caps, profile_name); + + } + + gst_caps_unref (caps); + return TRUE; +} + +static gboolean +bus_callback (GstBus * bus, GstMessage * message, gpointer data) +{ + GMainLoop *loop = data; + switch (GST_MESSAGE_TYPE (message)) { + case GST_MESSAGE_STATE_CHANGED: + { + if (GST_MESSAGE_SRC (message) == GST_OBJECT_CAST (pipeline)) { + gchar *dotname; + GstState old, new, pending; + + gst_message_parse_state_changed (message, &old, &new, &pending); + + if (new == GST_STATE_PLAYING) { + GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline), + GST_DEBUG_GRAPH_SHOW_ALL, "gst-validate-transcode.playing"); + } + + dotname = g_strdup_printf ("gst-validate-transcoding.%s_%s", + gst_element_state_get_name (old), gst_element_state_get_name (new)); + + GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline), + GST_DEBUG_GRAPH_SHOW_ALL, dotname); + g_free (dotname); + } + break; + } + case GST_MESSAGE_ERROR: + { + g_main_loop_quit (loop); + break; + } + case GST_MESSAGE_EOS: + if (!g_getenv ("GST_VALIDATE_SCENARIO")) + g_main_loop_quit (loop); + break; + case GST_MESSAGE_BUFFERING:{ + gint percent; + + if (!buffering) { + g_print ("\n"); + } + + gst_message_parse_buffering (message, &percent); + g_print ("%s %d%% \r", "Buffering...", percent); + + /* no state management needed for live pipelines */ + if (is_live) + break; + + if (percent == 100) { + /* a 100% message means buffering is done */ + if (buffering) { + buffering = FALSE; + gst_element_set_state (pipeline, GST_STATE_PLAYING); + } + } else { + /* buffering... */ + if (!buffering) { + gst_element_set_state (pipeline, GST_STATE_PAUSED); + buffering = TRUE; + } + } + break; + } + case GST_MESSAGE_REQUEST_STATE: + { + GstState state; + + gst_message_parse_request_state (message, &state); + + if (GST_IS_VALIDATE_SCENARIO (GST_MESSAGE_SRC (message)) + && state == GST_STATE_NULL) { + GST_VALIDATE_REPORT (GST_MESSAGE_SRC (message), + SCENARIO_ACTION_EXECUTION_ISSUE, + "Force stopping a transcoding pipeline is not recommanded" + " you should make sure to finalize it using a EOS event"); + + gst_validate_printf (pipeline, "State change request NULL, " + "quiting mainloop\n"); + g_main_loop_quit (mainloop); + } + break; + } + default: + break; + } + + return TRUE; +} + +static void +pad_added_cb (GstElement * uridecodebin, GstPad * pad, GstElement * encodebin) +{ + GstCaps *caps; + GstPad *sinkpad = NULL; + + caps = gst_pad_query_caps (pad, NULL); + + /* Ask encodebin for a compatible pad */ + GST_DEBUG_OBJECT (uridecodebin, "Pad added, caps: %" GST_PTR_FORMAT, caps); + + g_signal_emit_by_name (encodebin, "request-pad", caps, &sinkpad); + if (caps) + gst_caps_unref (caps); + + if (sinkpad == NULL) { + GST_WARNING ("Couldn't get an encoding pad for pad %s:%s\n", + GST_DEBUG_PAD_NAME (pad)); + return; + } + + if (G_UNLIKELY (gst_pad_link (pad, sinkpad) != GST_PAD_LINK_OK)) { + GstCaps *othercaps = gst_pad_get_current_caps (sinkpad); + caps = gst_pad_get_current_caps (pad); + + GST_ERROR ("Couldn't link pads \n\n%" GST_PTR_FORMAT "\n\n and \n\n %" + GST_PTR_FORMAT "\n\n", caps, othercaps); + + gst_caps_unref (caps); + gst_caps_unref (othercaps); + } + + gst_object_unref (sinkpad); + return; +} + +static void +create_transcoding_pipeline (gchar * uri, gchar * outuri) +{ + GstElement *src, *sink; + + pipeline = gst_pipeline_new ("encoding-pipeline"); + src = gst_element_factory_make ("uridecodebin", NULL); + + encodebin = gst_element_factory_make ("encodebin", NULL); + g_object_set (encodebin, "avoid-reencoding", !force_reencoding, NULL); + sink = gst_element_make_from_uri (GST_URI_SINK, outuri, "sink", NULL); + g_assert (sink); + + g_object_set (src, "uri", uri, NULL); + g_object_set (encodebin, "profile", encoding_profile, NULL); + + g_signal_connect (src, "pad-added", G_CALLBACK (pad_added_cb), encodebin); + + gst_bin_add_many (GST_BIN (pipeline), src, encodebin, sink, NULL); + gst_element_link (encodebin, sink); +} + +static gboolean +_parse_encoding_profile (const gchar * option_name, const gchar * value, + gpointer udata, GError ** error) +{ + GstCaps *caps; + char *preset_name = NULL; + gchar **restriction_format, **preset_v; + + guint i, presence = 0; + GstCaps *restrictioncaps = NULL; + gchar **strpresence_v, **strcaps_v = g_strsplit (value, ":", 0); + + if (strcaps_v[0] && *strcaps_v[0]) { + caps = gst_caps_from_string (strcaps_v[0]); + if (caps == NULL) { + g_printerr ("Could not parse caps %s", strcaps_v[0]); + return FALSE; + } + encoding_profile = + GST_ENCODING_PROFILE (gst_encoding_container_profile_new + ("User profile", "User profile", caps, NULL)); + gst_caps_unref (caps); + } else { + encoding_profile = NULL; + } + + for (i = 1; strcaps_v[i]; i++) { + GstEncodingProfile *profile = NULL; + gchar *strcaps, *strpresence; + + restriction_format = g_strsplit (strcaps_v[i], "->", 0); + if (restriction_format[1]) { + restrictioncaps = gst_caps_from_string (restriction_format[0]); + strcaps = g_strdup (restriction_format[1]); + } else { + restrictioncaps = NULL; + strcaps = g_strdup (restriction_format[0]); + } + g_strfreev (restriction_format); + + preset_v = g_strsplit (strcaps, "+", 0); + if (preset_v[1]) { + strpresence = preset_v[1]; + g_free (strcaps); + strcaps = g_strdup (preset_v[0]); + } else { + strpresence = preset_v[0]; + } + + strpresence_v = g_strsplit (strpresence, "|", 0); + if (strpresence_v[1]) { /* We have a presence */ + gchar *endptr; + + if (preset_v[1]) { /* We have preset and presence */ + preset_name = g_strdup (strpresence_v[0]); + } else { /* We have a presence but no preset */ + g_free (strcaps); + strcaps = g_strdup (strpresence_v[0]); + } + + presence = strtoll (strpresence_v[1], &endptr, 10); + if (endptr == strpresence_v[1]) { + g_printerr ("Wrong presence %s\n", strpresence_v[1]); + + return FALSE; + } + } else { /* We have no presence */ + if (preset_v[1]) { /* Not presence but preset */ + preset_name = g_strdup (preset_v[1]); + g_free (strcaps); + strcaps = g_strdup (preset_v[0]); + } /* Else we have no presence nor preset */ + } + g_strfreev (strpresence_v); + g_strfreev (preset_v); + + GST_DEBUG ("Creating preset with restrictions: %" GST_PTR_FORMAT + ", caps: %s, preset %s, presence %d", restrictioncaps, strcaps, + preset_name ? preset_name : "none", presence); + + caps = gst_caps_from_string (strcaps); + g_free (strcaps); + if (caps == NULL) { + g_warning ("Could not create caps for %s", strcaps_v[i]); + + return FALSE; + } + + all_raw_caps = g_list_append (all_raw_caps, gst_caps_copy (caps)); + if (g_str_has_prefix (strcaps_v[i], "audio/")) { + profile = GST_ENCODING_PROFILE (gst_encoding_audio_profile_new (caps, + preset_name, restrictioncaps, presence)); + } else if (g_str_has_prefix (strcaps_v[i], "video/") || + g_str_has_prefix (strcaps_v[i], "image/")) { + profile = GST_ENCODING_PROFILE (gst_encoding_video_profile_new (caps, + preset_name, restrictioncaps, presence)); + } + + g_free (preset_name); + gst_caps_unref (caps); + if (restrictioncaps) + gst_caps_unref (restrictioncaps); + + if (profile == NULL) { + g_warning ("No way to create a preset for caps: %s", strcaps_v[i]); + + return FALSE; + } + + if (encoding_profile) { + if (gst_encoding_container_profile_add_profile + (GST_ENCODING_CONTAINER_PROFILE (encoding_profile), + profile) == FALSE) { + g_warning ("Can not create a preset for caps: %s", strcaps_v[i]); + + return FALSE; + } + } else { + encoding_profile = profile; + } + } + g_strfreev (strcaps_v); + + return TRUE; +} + +static void +_register_actions (void) +{ +/* *INDENT-OFF* */ + gst_validate_register_action_type ("set-restriction", "validate-transcoding", _execute_set_restriction, + (GstValidateActionParameter []) { + { + .name = "restriction-caps", + .description = "The restriction caps to set on the encodebin" + "encoding profile.\nSee gst_encoding_profile_set_restriction()", + .mandatory = TRUE, + .types = "GstCaps serialized as a string" + }, + {NULL} + }, + "Change the restriction caps on the fly", + FALSE); + + gst_validate_register_action_type ("video-request-key-unit", "validate-transcoding", + _execute_request_key_unit, + (GstValidateActionParameter []) { + { + .name = "direction", + .description = "The direction for the event to travel, should be in\n" + " * [upstream, downstream]", + .mandatory = TRUE, + .types = "string", + NULL + }, + { + .name = "running-time", + .description = "The running_time can be set to request a new key unit at a specific running_time.\n" + "If not set, GST_CLOCK_TIME_NONE will be used so upstream elements will produce a new key unit" + "as soon as possible.", + .mandatory = FALSE, + .types = "double or string", + .possible_variables = "position: The current position in the stream\n" + "duration: The duration of the stream", + NULL + }, + { + .name = "all-headers", + .description = "TRUE to produce headers when starting a new key unit", + .mandatory = TRUE, + .types = "boolean", + NULL + }, + { + .name = "count", + .description = "integer that can be used to number key units", + .mandatory = TRUE, + .types = "int", + NULL + }, + {NULL} + }, + "Request a video key unit", FALSE); +/* *INDENT-ON* */ +} + +int +main (int argc, gchar ** argv) +{ + guint i; + GstBus *bus; + GstValidateRunner *runner; + GstValidateMonitor *monitor; + GOptionContext *ctx; + int rep_err; + GstStateChangeReturn sret; + gchar *output_file = NULL; + +#ifdef G_OS_UNIX + guint signal_watch_id; +#endif + + GError *err = NULL; + const gchar *scenario = NULL, *configs = NULL; + gboolean want_help = FALSE; + gboolean list_scenarios = FALSE, inspect_action_type = FALSE; + + GOptionEntry options[] = { + {"output-format", 'o', 0, G_OPTION_ARG_CALLBACK, &_parse_encoding_profile, + "Set the properties to use for the encoding profile " + "(in case of transcoding.) For example:\n" + "video/mpegts:video/x-raw-yuv,width=1920,height=1080->video/x-h264:audio/x-ac3\n" + "A preset name can be used by adding +presetname, eg:\n" + "video/webm:video/x-vp8+mypreset:audio/x-vorbis\n" + "The presence property of the profile can be specified with |, eg:\n" + "video/webm:video/x-vp8|:audio/x-vorbis\n", + "properties-values"}, + {"set-scenario", '\0', 0, G_OPTION_ARG_STRING, &scenario, + "Let you set a scenario, it can be a full path to a scenario file" + " or the name of the scenario (name of the file without the" + " '.scenario' extension).", NULL}, + {"set-configs", '\0', 0, G_OPTION_ARG_STRING, &configs, + "Let you set a config scenario, the scenario needs to be set as 'config" + "' you can specify a list of scenario separated by ':'" + " it will override the GST_VALIDATE_SCENARIO environment variable,", + NULL}, + {"eos-on-shutdown", 'e', 0, G_OPTION_ARG_NONE, &eos_on_shutdown, + "If an EOS event should be sent to the pipeline if an interrupt is " + "received, instead of forcing the pipeline to stop. Sending an EOS " + "will allow the transcoding to finish the files properly before " + "exiting.", NULL}, + {"list-scenarios", 'l', 0, G_OPTION_ARG_NONE, &list_scenarios, + "List the avalaible scenarios that can be run", NULL}, + {"inspect-action-type", 't', 0, G_OPTION_ARG_NONE, &inspect_action_type, + "Inspect the avalaible action types with which to write scenarios" + " if no parameter passed, it will list all avalaible action types" + " otherwize will print the full description of the wanted types", + NULL}, + {"scenarios-defs-output-file", '\0', 0, G_OPTION_ARG_FILENAME, + &output_file, "The output file to store scenarios details. " + "Implies --list-scenario", + NULL}, + {"force-reencoding", 'r', 0, G_OPTION_ARG_NONE, &force_reencoding, + "Whether to try to force reencoding, meaning trying to only remux " + "if possible(default: TRUE)", NULL}, + {NULL} + }; + + /* There is a bug that make gst_init remove the help param when initializing, + * it is FIXED in 1.0 */ + for (i = 1; i < argc; i++) { + if (!g_strcmp0 (argv[i], "--help") || !g_strcmp0 (argv[i], "-h")) + want_help = TRUE; + } + + if (!want_help) + gst_init (&argc, &argv); + + g_set_prgname ("gst-validate-transcoding-" GST_API_VERSION); + ctx = g_option_context_new ("[input-uri] [output-uri]"); + g_option_context_set_summary (ctx, "Transcodes input-uri to output-uri, " + "using the given encoding profile. The pipeline will be monitored for " + "possible issues detection using the gst-validate lib." + "\nCan also perform file conformance" + "tests after transcoding to make sure the result is correct"); + g_option_context_add_main_entries (ctx, options, NULL); + if (want_help) { + g_option_context_add_group (ctx, gst_init_get_option_group ()); + } + + if (!g_option_context_parse (ctx, &argc, &argv, &err)) { + g_printerr ("Error initializing: %s\n", err->message); + g_option_context_free (ctx); + exit (1); + } + + g_option_context_free (ctx); + + if (scenario || configs) { + gchar *scenarios; + + if (scenario) + scenarios = g_strjoin (":", scenario, configs, NULL); + else + scenarios = g_strdup (configs); + + g_setenv ("GST_VALIDATE_SCENARIO", scenarios, TRUE); + g_free (scenarios); + } + + gst_validate_init (); + + if (list_scenarios || output_file) { + if (gst_validate_list_scenarios (argv + 1, argc - 1, output_file)) + return 1; + return 0; + } + + + _register_actions (); + + if (inspect_action_type) { + if (gst_validate_print_action_types ((const gchar **) argv + 1, argc - 1)) + return 0; + + return -1; + } + + if (argc != 3) { + g_printerr ("%i arguments recived, 2 expected.\n" + "You should run the test using:\n" + " ./gst-validate-transcoding-0.10 [options]\n", + argc - 1); + return 1; + } + + if (encoding_profile == NULL) { + GST_INFO ("Creating default encoding profile"); + + _parse_encoding_profile ("encoding-profile", + "application/ogg:video/x-theora:audio/x-vorbis", NULL, NULL); + } + + /* Create the pipeline */ + create_transcoding_pipeline (argv[1], argv[2]); + +#ifdef G_OS_UNIX + signal_watch_id = + g_unix_signal_add (SIGINT, (GSourceFunc) intr_handler, pipeline); +#endif + + runner = gst_validate_runner_new (); + monitor = + gst_validate_monitor_factory_create (GST_OBJECT_CAST (pipeline), runner, + NULL); + gst_validate_reporter_set_handle_g_logs (GST_VALIDATE_REPORTER (monitor)); + mainloop = g_main_loop_new (NULL, FALSE); + + if (!runner) { + g_printerr ("Failed to setup Validate Runner\n"); + exit (1); + } + + bus = gst_element_get_bus (pipeline); + gst_bus_add_signal_watch (bus); + g_signal_connect (bus, "message", (GCallback) bus_callback, mainloop); + + g_print ("Starting pipeline\n"); + sret = gst_element_set_state (pipeline, GST_STATE_PLAYING); + switch (sret) { + case GST_STATE_CHANGE_FAILURE: + /* ignore, we should get an error message posted on the bus */ + g_print ("Pipeline failed to go to PLAYING state\n"); + ret = -1; + goto exit; + case GST_STATE_CHANGE_NO_PREROLL: + g_print ("Pipeline is live.\n"); + is_live = TRUE; + break; + case GST_STATE_CHANGE_ASYNC: + g_print ("Prerolling...\r"); + break; + default: + break; + } + + g_main_loop_run (mainloop); + + rep_err = gst_validate_runner_exit (runner, TRUE); + if (ret == 0) + ret = rep_err; + +exit: + gst_bus_remove_signal_watch (bus); + gst_object_unref (bus); + + gst_element_set_state (pipeline, GST_STATE_NULL); + g_main_loop_unref (mainloop); + g_object_unref (pipeline); + g_object_unref (monitor); + g_object_unref (runner); + +#ifdef G_OS_UNIX + g_source_remove (signal_watch_id); +#endif + gst_deinit (); + gst_validate_deinit (); + + g_print ("\n=======> Test %s (Return value: %i)\n\n", + ret == 0 ? "PASSED" : "FAILED", ret); + return ret; +} diff --git a/validate/tools/gst-validate.c b/validate/tools/gst-validate.c new file mode 100644 index 0000000..f11dbbc --- /dev/null +++ b/validate/tools/gst-validate.c @@ -0,0 +1,635 @@ +/* GStreamer + * + * Copyright (C) 2013 Collabora Ltd. + * Author: Thiago Sousa Santos + * + * gst-validate.c - Validate CLI launch line tool + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef G_OS_UNIX +#include +#endif + +static gint ret = 0; +static GMainLoop *mainloop; +static GstElement *pipeline; +static gboolean buffering = FALSE; + +static gboolean is_live = FALSE; + +#ifdef G_OS_UNIX +static gboolean +intr_handler (gpointer user_data) +{ + g_print ("interrupt received.\n"); + + GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline), + GST_DEBUG_GRAPH_SHOW_ALL, "gst-validate.interupted"); + + g_main_loop_quit (mainloop); + + ret = SIGINT; + + /* Keep signal handler, it will be removed later anyway */ + return TRUE; +} +#endif /* G_OS_UNIX */ + +static gboolean +bus_callback (GstBus * bus, GstMessage * message, gpointer data) +{ + GMainLoop *loop = data; + + switch (GST_MESSAGE_TYPE (message)) { + case GST_MESSAGE_ERROR: + { + GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline), + GST_DEBUG_GRAPH_SHOW_ALL, "gst-validate.error"); + + g_main_loop_quit (loop); + break; + } + case GST_MESSAGE_EOS: + if (!g_getenv ("GST_VALIDATE_SCENARIO")) + g_main_loop_quit (loop); + break; + case GST_MESSAGE_ASYNC_DONE: + break; + case GST_MESSAGE_STATE_CHANGED: + if (GST_MESSAGE_SRC (message) == GST_OBJECT (pipeline)) { + GstState oldstate, newstate, pending; + gchar *dump_name; + gchar *state_transition_name; + + gst_message_parse_state_changed (message, &oldstate, &newstate, + &pending); + + GST_DEBUG ("State changed (old: %s, new: %s, pending: %s)", + gst_element_state_get_name (oldstate), + gst_element_state_get_name (newstate), + gst_element_state_get_name (pending)); + + state_transition_name = g_strdup_printf ("%s_%s", + gst_element_state_get_name (oldstate), + gst_element_state_get_name (newstate)); + dump_name = g_strconcat ("ges-launch.", state_transition_name, NULL); + + + GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline), + GST_DEBUG_GRAPH_SHOW_ALL, dump_name); + + g_free (dump_name); + g_free (state_transition_name); + } + + break; + case GST_MESSAGE_WARNING:{ + GError *gerror; + gchar *debug; + gchar *name = gst_object_get_path_string (GST_MESSAGE_SRC (message)); + + /* dump graph on warning */ + GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline), + GST_DEBUG_GRAPH_SHOW_ALL, "gst-validate.warning"); + + gst_message_parse_warning (message, &gerror, &debug); + g_print ("WARNING: from element %s: %s\n", name, gerror->message); + if (debug) + g_print ("Additional debug info:\n%s\n", debug); + + g_error_free (gerror); + g_free (debug); + g_free (name); + break; + } + case GST_MESSAGE_BUFFERING:{ + gint percent; + GstBufferingMode mode; + + if (!buffering) { + g_print ("\n"); + } + + gst_message_parse_buffering (message, &percent); + gst_message_parse_buffering_stats (message, &mode, NULL, NULL, NULL); + g_print ("%s %d%% \r", "Buffering...", percent); + + /* no state management needed for live pipelines */ + if (mode == GST_BUFFERING_LIVE) { + is_live = TRUE; + break; + } + + if (percent == 100) { + /* a 100% message means buffering is done */ + if (buffering) { + buffering = FALSE; + g_print ("Done buffering, setting pipeline to PLAYING\n"); + gst_element_set_state (pipeline, GST_STATE_PLAYING); + } + } else { + /* buffering... */ + if (!buffering) { + g_print ("Start buffering, setting pipeline to PAUSED\n"); + gst_element_set_state (pipeline, GST_STATE_PAUSED); + buffering = TRUE; + } + } + break; + } + case GST_MESSAGE_REQUEST_STATE: + { + GstState state; + + gst_message_parse_request_state (message, &state); + + if (GST_IS_VALIDATE_SCENARIO (GST_MESSAGE_SRC (message)) + && state == GST_STATE_NULL) { + gst_validate_printf (GST_MESSAGE_SRC (message), + "State change request NULL, " "quiting mainloop\n"); + g_main_loop_quit (mainloop); + } + break; + } + default: + break; + } + + return TRUE; +} + +static gboolean +_is_playbin_pipeline (int argc, gchar ** argv) +{ + gint i; + + for (i = 0; i < argc; i++) { + if (g_strcmp0 (argv[i], "playbin") == 0) { + return TRUE; + } + } + + return FALSE; +} + +static gboolean +_execute_set_subtitles (GstValidateScenario * scenario, + GstValidateAction * action) +{ + gchar *uri, *fname; + GFile *tmpfile, *folder; + const gchar *subtitle_file, *subtitle_dir; + + subtitle_file = gst_structure_get_string (action->structure, "subtitle-file"); + g_return_val_if_fail (subtitle_file != NULL, FALSE); + subtitle_dir = gst_structure_get_string (action->structure, "subtitle-dir"); + + g_object_get (scenario->pipeline, "current-uri", &uri, NULL); + tmpfile = g_file_new_for_uri (uri); + g_free (uri); + + folder = g_file_get_parent (tmpfile); + + fname = g_strdup_printf ("%s%s%s%s", + subtitle_dir ? subtitle_dir : "", + subtitle_dir ? G_DIR_SEPARATOR_S : "", + g_file_get_basename (tmpfile), subtitle_file); + gst_object_unref (tmpfile); + + tmpfile = g_file_get_child (folder, fname); + g_free (fname); + gst_object_unref (folder); + + uri = g_file_get_uri (tmpfile); + gst_validate_printf (action, "Setting subtitle file to: %s", uri); + g_object_set (scenario->pipeline, "suburi", uri, NULL); + g_free (uri); + + return TRUE; +} + +static GstPadProbeReturn +_check_pad_selection_done (GstPad * pad, GstPadProbeInfo * info, + GstValidateAction * action) +{ + if (GST_BUFFER_FLAG_IS_SET (info->data, GST_BUFFER_FLAG_DISCONT)) { + gst_validate_action_set_done (action); + + return GST_PAD_PROBE_REMOVE; + } + + return GST_PAD_PROBE_OK; +} + +static gboolean +_execute_switch_track (GstValidateScenario * scenario, + GstValidateAction * action) +{ + gint index, n; + GstPad *srcpad; + GstElement *combiner; + GstPad *oldpad, *newpad; + const gchar *type, *str_index; + + gint flags, current, tflag; + gchar *tmp, *current_txt; + + gint res = GST_VALIDATE_EXECUTE_ACTION_OK; + gboolean relative = FALSE, disabling = FALSE; + + if (!(type = gst_structure_get_string (action->structure, "type"))) + type = "audio"; + + tflag = + gst_validate_utils_flags_from_str (g_type_from_name ("GstPlayFlags"), + type); + current_txt = g_strdup_printf ("current-%s", type); + + tmp = g_strdup_printf ("n-%s", type); + g_object_get (scenario->pipeline, "flags", &flags, tmp, &n, + current_txt, ¤t, NULL); + + g_free (tmp); + + if (gst_structure_has_field (action->structure, "disable")) { + disabling = TRUE; + flags &= ~tflag; + index = -1; + } else if (!(str_index = + gst_structure_get_string (action->structure, "index"))) { + if (!gst_structure_get_int (action->structure, "index", &index)) { + GST_WARNING ("No index given, defaulting to +1"); + index = 1; + relative = TRUE; + } + } else { + relative = strchr ("+-", str_index[0]) != NULL; + index = g_ascii_strtoll (str_index, NULL, 10); + } + + if (relative) { /* We are changing track relatively to current track */ + index = current + index; + if (current >= n) + index = -2; + } + + if (!disabling) { + GstState state, next; + tmp = g_strdup_printf ("get-%s-pad", type); + g_signal_emit_by_name (G_OBJECT (scenario->pipeline), tmp, current, + &oldpad); + g_signal_emit_by_name (G_OBJECT (scenario->pipeline), tmp, index, &newpad); + + gst_validate_printf (action, "Switching to track number: %i," + " (from %s:%s to %s:%s)\n", index, GST_DEBUG_PAD_NAME (oldpad), + GST_DEBUG_PAD_NAME (newpad)); + flags |= tflag; + g_free (tmp); + + if (gst_element_get_state (scenario->pipeline, &state, &next, 0) && + state == GST_STATE_PLAYING && next == GST_STATE_VOID_PENDING) { + + combiner = GST_ELEMENT (gst_object_get_parent (GST_OBJECT (newpad))); + srcpad = gst_element_get_static_pad (combiner, "src"); + gst_object_unref (combiner); + + gst_pad_add_probe (srcpad, + GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BUFFER_LIST, + (GstPadProbeCallback) _check_pad_selection_done, action, NULL); + gst_object_unref (srcpad); + + res = GST_VALIDATE_EXECUTE_ACTION_ASYNC; + } + + } else { + gst_validate_printf (action, "Disabling track type %s", type); + } + + g_object_set (scenario->pipeline, "flags", flags, current_txt, index, NULL); + g_free (current_txt); + + return res; +} + +static void +_register_playbin_actions (void) +{ +/* *INDENT-OFF* */ + gst_validate_register_action_type ("set-subtitle", "validate-launcher", _execute_set_subtitles, + (GstValidateActionParameter []) { + { + .name = "subtitle-file", + .description = "Sets a subtitles file on a playbin pipeline", + .mandatory = TRUE, + .types = "string (A URI)", + NULL + }, + {NULL} + }, + "Action to set a subtitle file to use on a playbin pipeline.\n" + "The subtitles file that will be used should will be specified\n" + "relatively to the playbin URI in use thanks to the subtitle-file\n" + "action property. You can also specify a folder with subtitle-dir\n" + "For example if playbin.uri='file://some/uri.mov\n" + "and action looks like 'set-subtitle, subtitle-file=en.srt'\n" + "the subtitle URI will be set to 'file:///some/uri.mov.en.srt'\n", + FALSE); + + /* Overriding default implementation */ + gst_validate_register_action_type ("switch-track", "validate-launcher", _execute_switch_track, + (GstValidateActionParameter []) { + { + .name = "type", + .description = "Selects which track type to change (can be 'audio', 'video'," + " or 'text').", + .mandatory = FALSE, + .types = "string", + .possible_variables = NULL, + .def = "audio", + }, + { + .name = "index", + .description = "Selects which track of this type to use: it can be either a number,\n" + "which will be the Nth track of the given type, or a number with a '+' or\n" + "'-' prefix, which means a relative change (eg, '+1' means 'next track',\n" + "'-1' means 'previous track')", + .mandatory = FALSE, + .types = "string: to switch track relatively\n" + "int: To use the actual index to use", + .possible_variables = NULL, + .def = "+1", + }, + {NULL} + }, + "The 'switch-track' command can be used to switch tracks.\n" + "The 'type' argument selects which track type to change (can be 'audio', 'video'," + " or 'text').\nThe 'index' argument selects which track of this type\n" + "to use: it can be either a number, which will be the Nth track of\n" + "the given type, or a number with a '+' or '-' prefix, which means\n" + "a relative change (eg, '+1' means 'next track', '-1' means 'previous\n" + "track'), note that you need to state that it is a string in the scenario file\n" + "prefixing it with (string).", FALSE); +/* *INDENT-ON* */ +} + +int +main (int argc, gchar ** argv) +{ + GError *err = NULL; + gchar *scenario = NULL, *configs = NULL, *media_info = NULL; + gboolean list_scenarios = FALSE, monitor_handles_state, + inspect_action_type = FALSE; + GstStateChangeReturn sret; + gchar *output_file = NULL; + +#ifdef G_OS_UNIX + guint signal_watch_id; +#endif + int rep_err; + + GOptionEntry options[] = { + {"set-scenario", '\0', 0, G_OPTION_ARG_STRING, &scenario, + "Let you set a scenario, it can be a full path to a scenario file" + " or the name of the scenario (name of the file without the" + " '.scenario' extension).", NULL}, + {"list-scenarios", 'l', 0, G_OPTION_ARG_NONE, &list_scenarios, + "List the avalaible scenarios that can be run", NULL}, + {"scenarios-defs-output-file", '\0', 0, G_OPTION_ARG_FILENAME, + &output_file, "The output file to store scenarios details. " + "Implies --list-scenario", + NULL}, + {"inspect-action-type", 't', 0, G_OPTION_ARG_NONE, &inspect_action_type, + "Inspect the avalaible action types with which to write scenarios" + " if no parameter passed, it will list all avalaible action types" + " otherwize will print the full description of the wanted types", + NULL}, + {"set-media-info", '\0', 0, G_OPTION_ARG_STRING, &media_info, + "Set a media_info XML file descriptor to share information about the" + " media file that will be reproduced.", + NULL}, + {"set-configs", '\0', 0, G_OPTION_ARG_STRING, &configs, + "Let you set a config scenario, the scenario needs to be set as 'config" + "' you can specify a list of scenario separated by ':'" + " it will override the GST_VALIDATE_SCENARIO environment variable.", + NULL}, + {NULL} + }; + GOptionContext *ctx; + gchar **argvn; + GstValidateRunner *runner; + GstValidateMonitor *monitor; + GstBus *bus; + + g_set_prgname ("gst-validate-" GST_API_VERSION); + ctx = g_option_context_new ("PIPELINE-DESCRIPTION"); + g_option_context_add_main_entries (ctx, options, NULL); + g_option_context_set_summary (ctx, "Runs a gst launch pipeline, adding " + "monitors to it to identify issues in the used elements. At the end" + " a report will be printed. To view issues as they are created, set" + " the env var GST_DEBUG=validate:2 and it will be printed " + "as gstreamer debugging"); + + if (argc == 1) { + g_print ("%s", g_option_context_get_help (ctx, FALSE, NULL)); + exit (1); + } + + if (!g_option_context_parse (ctx, &argc, &argv, &err)) { + g_printerr ("Error initializing: %s\n", err->message); + g_option_context_free (ctx); + g_clear_error (&err); + exit (1); + } + + if (scenario || configs) { + gchar *scenarios; + + if (scenario) + scenarios = g_strjoin (":", scenario, configs, NULL); + else + scenarios = g_strdup (configs); + + g_setenv ("GST_VALIDATE_SCENARIO", scenarios, TRUE); + g_free (scenarios); + g_free (scenario); + g_free (configs); + } + + gst_init (&argc, &argv); + gst_validate_init (); + + if (list_scenarios || output_file) { + if (gst_validate_list_scenarios (argv + 1, argc - 1, output_file)) + return 1; + return 0; + } + + if (inspect_action_type) { + _register_playbin_actions (); + + if (!gst_validate_print_action_types ((const gchar **) argv + 1, argc - 1)) { + GST_ERROR ("Could not print all wanted types"); + return -1; + } + + return 0; + } + + if (argc == 1) { + g_print ("%s", g_option_context_get_help (ctx, FALSE, NULL)); + g_option_context_free (ctx); + exit (1); + } + + g_option_context_free (ctx); + + /* Create the pipeline */ + argvn = g_new0 (char *, argc); + memcpy (argvn, argv + 1, sizeof (char *) * (argc - 1)); + pipeline = (GstElement *) gst_parse_launchv ((const gchar **) argvn, &err); + g_free (argvn); + if (!pipeline) { + g_print ("Failed to create pipeline: %s\n", + err ? err->message : "unknown reason"); + g_clear_error (&err); + exit (1); + } + if (!GST_IS_PIPELINE (pipeline)) { + GstElement *new_pipeline = gst_pipeline_new (""); + + gst_bin_add (GST_BIN (new_pipeline), pipeline); + pipeline = new_pipeline; + } + + gst_pipeline_set_auto_flush_bus (GST_PIPELINE (pipeline), FALSE); +#ifdef G_OS_UNIX + signal_watch_id = + g_unix_signal_add (SIGINT, (GSourceFunc) intr_handler, pipeline); +#endif + + if (_is_playbin_pipeline (argc, argv + 1)) { + _register_playbin_actions (); + } + + runner = gst_validate_runner_new (); + if (!runner) { + g_printerr ("Failed to setup Validate Runner\n"); + exit (1); + } + + monitor = gst_validate_monitor_factory_create (GST_OBJECT_CAST (pipeline), + runner, NULL); + gst_validate_reporter_set_handle_g_logs (GST_VALIDATE_REPORTER (monitor)); + + if (media_info) { + GError *err = NULL; + GstMediaDescriptorParser *parser = gst_media_descriptor_parser_new (runner, + media_info, &err); + + if (parser == NULL) { + GST_ERROR ("Could not use %s as a media-info file (error: %s)", + media_info, err ? err->message : "Unknown error"); + + g_free (media_info); + exit (1); + } + + gst_validate_monitor_set_media_descriptor (monitor, + GST_MEDIA_DESCRIPTOR (parser)); + gst_object_unref (parser); + g_free (media_info); + } + + mainloop = g_main_loop_new (NULL, FALSE); + bus = gst_element_get_bus (pipeline); + gst_bus_add_signal_watch (bus); + g_signal_connect (bus, "message", (GCallback) bus_callback, mainloop); + + g_print ("Starting pipeline\n"); + g_object_get (monitor, "handles-states", &monitor_handles_state, NULL); + if (monitor_handles_state == FALSE) { + sret = gst_element_set_state (pipeline, GST_STATE_PLAYING); + switch (sret) { + case GST_STATE_CHANGE_FAILURE: + /* ignore, we should get an error message posted on the bus */ + g_print ("Pipeline failed to go to PLAYING state\n"); + gst_element_set_state (pipeline, GST_STATE_NULL); + ret = -1; + goto exit; + case GST_STATE_CHANGE_NO_PREROLL: + g_print ("Pipeline is live.\n"); + is_live = TRUE; + break; + case GST_STATE_CHANGE_ASYNC: + g_print ("Prerolling...\r"); + break; + default: + break; + } + g_print ("Pipeline started\n"); + } else { + g_print ("Letting scenario handle set state\n"); + } + + g_main_loop_run (mainloop); + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_element_get_state (pipeline, NULL, NULL, GST_CLOCK_TIME_NONE); + + /* Clean the bus */ + gst_bus_set_flushing (bus, TRUE); + gst_bus_remove_signal_watch (bus); + gst_object_unref (bus); + + rep_err = gst_validate_runner_exit (runner, TRUE); + if (ret == 0) { + ret = rep_err; + if (rep_err != 0) + g_print ("Returning %d as error where found", rep_err); + } + +exit: + g_main_loop_unref (mainloop); + g_object_unref (pipeline); + g_object_unref (runner); + g_object_unref (monitor); + g_clear_error (&err); +#ifdef G_OS_UNIX + g_source_remove (signal_watch_id); +#endif + gst_deinit (); + + g_print ("\n=======> Test %s (Return value: %i)\n\n", + ret == 0 ? "PASSED" : "FAILED", ret); + + gst_validate_deinit (); + return ret; +} -- 2.7.4