From f5f21e5e8b1d174eeed28dbbc192034f5ec0d745 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Tim-Philipp=20M=C3=BCller?= Date: Thu, 10 Aug 2017 11:15:26 +0100 Subject: [PATCH] libs: check: sprinkle some GST_EXPORT Have to modify libcheck header a bit to avoid warnings about duplicate 'extern extern'. Also needs some additions to the libcheck meson.build file to define CK_EXP_DLL when building the static libcheck. --- libs/gst/check/gstbufferstraw.h | 5 ++ libs/gst/check/gstcheck.h | 87 ++++++++++++++++++++++++++++------ libs/gst/check/gstconsistencychecker.h | 6 ++- libs/gst/check/gstharness.h | 79 ++++++++++++++++++++++++++++++ libs/gst/check/gsttestclock.c | 4 -- libs/gst/check/gsttestclock.h | 18 ++++++- libs/gst/check/libcheck/check.h.in | 8 ++-- 7 files changed, 181 insertions(+), 26 deletions(-) diff --git a/libs/gst/check/gstbufferstraw.h b/libs/gst/check/gstbufferstraw.h index 84ccc7f..d366a85 100644 --- a/libs/gst/check/gstbufferstraw.h +++ b/libs/gst/check/gstbufferstraw.h @@ -26,8 +26,13 @@ G_BEGIN_DECLS +GST_EXPORT void gst_buffer_straw_start_pipeline (GstElement * bin, GstPad * pad); + +GST_EXPORT GstBuffer * gst_buffer_straw_get_buffer (GstElement * bin, GstPad * pad); + +GST_EXPORT void gst_buffer_straw_stop_pipeline (GstElement * bin, GstPad * pad); G_END_DECLS diff --git a/libs/gst/check/gstcheck.h b/libs/gst/check/gstcheck.h index 63c05b6..da2e30d 100644 --- a/libs/gst/check/gstcheck.h +++ b/libs/gst/check/gstcheck.h @@ -29,13 +29,14 @@ #include #include -#include - #include +#define CK_DLL_EXP GST_EXPORT +#include + G_BEGIN_DECLS -GST_DEBUG_CATEGORY_EXTERN (check_debug); +GST_EXPORT GstDebugCategory *check_debug; #define GST_CAT_DEFAULT check_debug /* logging function for tests @@ -43,17 +44,17 @@ GST_DEBUG_CATEGORY_EXTERN (check_debug); * a gst unit test can be run with GST_TEST_DEBUG env var set to see the * messages */ -extern gboolean _gst_check_threads_running; -extern gboolean _gst_check_raised_critical; -extern gboolean _gst_check_raised_warning; -extern gboolean _gst_check_expecting_log; -extern gboolean _gst_check_list_tests; +GST_EXPORT gboolean _gst_check_threads_running; +GST_EXPORT gboolean _gst_check_raised_critical; +GST_EXPORT gboolean _gst_check_raised_warning; +GST_EXPORT gboolean _gst_check_expecting_log; +GST_EXPORT gboolean _gst_check_list_tests; /* global variables used in test methods */ -extern GList * buffers; +GST_EXPORT GList * buffers; -extern GMutex check_mutex; -extern GCond check_cond; +GST_EXPORT GMutex check_mutex; +GST_EXPORT GCond check_cond; typedef struct { @@ -82,59 +83,114 @@ typedef struct _GstCheckLogFilter GstCheckLogFilter; typedef gboolean (*GstCheckLogFilterFunc) (const gchar * log_domain, GLogLevelFlags log_level, const gchar * message, gpointer user_data); +GST_EXPORT void gst_check_init (int *argc, char **argv[]); +GST_EXPORT GstCheckLogFilter * gst_check_add_log_filter (const gchar * log, GLogLevelFlags log_level, GRegex * regex, GstCheckLogFilterFunc func, gpointer user_data, GDestroyNotify destroy_data); + +GST_EXPORT void gst_check_remove_log_filter (GstCheckLogFilter * filter); + +GST_EXPORT void gst_check_clear_log_filter (void); +GST_EXPORT GstFlowReturn gst_check_chain_func (GstPad * pad, GstObject * parent, GstBuffer * buffer); +GST_EXPORT void gst_check_message_error (GstMessage * message, GstMessageType type, GQuark domain, gint code); +GST_EXPORT GstElement *gst_check_setup_element (const gchar * factory); + +GST_EXPORT void gst_check_teardown_element (GstElement * element); + +GST_EXPORT GstPad *gst_check_setup_src_pad (GstElement * element, GstStaticPadTemplate * tmpl); + +GST_EXPORT GstPad *gst_check_setup_src_pad_from_template (GstElement * element, GstPadTemplate * tmpl); + +GST_EXPORT GstPad * gst_check_setup_src_pad_by_name (GstElement * element, GstStaticPadTemplate * tmpl, const gchar *name); + +GST_EXPORT GstPad * gst_check_setup_src_pad_by_name_from_template (GstElement * element, GstPadTemplate * tmpl, const gchar *name); + +GST_EXPORT GstPad *gst_check_setup_sink_pad (GstElement * element, GstStaticPadTemplate * tmpl); + +GST_EXPORT GstPad *gst_check_setup_sink_pad_from_template (GstElement * element, GstPadTemplate * tmpl); + +GST_EXPORT GstPad * gst_check_setup_sink_pad_by_name (GstElement * element, GstStaticPadTemplate * tmpl, const gchar *name); + +GST_EXPORT GstPad * gst_check_setup_sink_pad_by_name_from_template (GstElement * element, GstPadTemplate * tmpl, const gchar *name); + +GST_EXPORT void gst_check_teardown_pad_by_name (GstElement * element, const gchar *name); + +GST_EXPORT void gst_check_teardown_src_pad (GstElement * element); + +GST_EXPORT void gst_check_drop_buffers (void); + +GST_EXPORT void gst_check_caps_equal (GstCaps * caps1, GstCaps * caps2); + +GST_EXPORT void gst_check_buffer_data (GstBuffer * buffer, gconstpointer data, gsize size); + +GST_EXPORT void gst_check_element_push_buffer_list (const gchar * element_name, GList * buffer_in, GstCaps * caps_in, GList * buffer_out, GstCaps * caps_out, GstFlowReturn last_flow_return); + +GST_EXPORT void gst_check_element_push_buffer (const gchar * element_name, GstBuffer * buffer_in, GstCaps * caps_in, GstBuffer * buffer_out, GstCaps *caps_out); + +GST_EXPORT void gst_check_teardown_sink_pad (GstElement * element); + +GST_EXPORT void gst_check_abi_list (GstCheckABIStruct list[], gboolean have_abi_sizes); + +GST_EXPORT gint gst_check_run_suite (Suite * suite, const gchar * name, const gchar * fname); + +GST_EXPORT void gst_check_setup_events (GstPad * srcpad, GstElement * element, GstCaps * caps, GstFormat format); + +GST_EXPORT void gst_check_setup_events_with_stream_id (GstPad * srcpad, GstElement * element, GstCaps * caps, GstFormat format, const gchar * stream_id); + +GST_EXPORT void gst_check_objects_destroyed_on_unref (gpointer object_to_unref, gpointer first_object, ...) G_GNUC_NULL_TERMINATED; + +GST_EXPORT void gst_check_object_destroyed_on_unref (gpointer object_to_unref); #define fail_unless_message_error(msg, domain, code) \ @@ -446,10 +502,10 @@ G_STMT_START { \ /*** * thread test macros and variables */ -extern GList *thread_list; -extern GMutex mutex; -extern GCond start_cond; /* used to notify main thread of thread startups */ -extern GCond sync_cond; /* used to synchronize all threads and main thread */ +GST_EXPORT GList *thread_list; +GST_EXPORT GMutex mutex; +GST_EXPORT GCond start_cond; /* used to notify main thread of thread startups */ +GST_EXPORT GCond sync_cond; /* used to synchronize all threads and main thread */ #define MAIN_START_THREADS(count, function, data) \ MAIN_INIT(); \ @@ -621,6 +677,7 @@ int main (int argc, char **argv) \ * GST_CHECKS environment variable (test function names globs, comma * separated), or GST_CHECKS_IGNORE with the same semantics */ +GST_EXPORT gboolean _gst_check_run_test_func (const gchar * func_name); static inline void diff --git a/libs/gst/check/gstconsistencychecker.h b/libs/gst/check/gstconsistencychecker.h index 875db6b..464c6a3 100644 --- a/libs/gst/check/gstconsistencychecker.h +++ b/libs/gst/check/gstconsistencychecker.h @@ -34,13 +34,17 @@ G_BEGIN_DECLS */ typedef struct _GstStreamConsistency GstStreamConsistency; - +GST_EXPORT GstStreamConsistency * gst_consistency_checker_new (GstPad * pad); + +GST_EXPORT gboolean gst_consistency_checker_add_pad (GstStreamConsistency * consist, GstPad * pad); +GST_EXPORT void gst_consistency_checker_reset (GstStreamConsistency * consist); +GST_EXPORT void gst_consistency_checker_free (GstStreamConsistency * consist); G_END_DECLS diff --git a/libs/gst/check/gstharness.h b/libs/gst/check/gstharness.h index 0937ba7..7773136 100644 --- a/libs/gst/check/gstharness.h +++ b/libs/gst/check/gstharness.h @@ -63,8 +63,10 @@ struct _GstHarness { /* Harness creation */ +GST_EXPORT GstHarness * gst_harness_new_empty (void); +GST_EXPORT void gst_harness_add_element_full (GstHarness * h, GstElement * element, GstStaticPadTemplate * hsrc, @@ -72,190 +74,256 @@ void gst_harness_add_element_full (GstHarness * h, GstStaticPadTemplate * hsink, const gchar * element_srcpad_name); +GST_EXPORT GstHarness * gst_harness_new_full (GstElement * element, GstStaticPadTemplate * hsrc, const gchar * element_sinkpad_name, GstStaticPadTemplate * hsink, const gchar * element_srcpad_name); +GST_EXPORT GstHarness * gst_harness_new_with_element (GstElement * element, const gchar * element_sinkpad_name, const gchar * element_srcpad_name); +GST_EXPORT GstHarness * gst_harness_new_with_padnames (const gchar * element_name, const gchar * element_sinkpad_name, const gchar * element_srcpad_name); +GST_EXPORT GstHarness * gst_harness_new_with_templates (const gchar * element_name, GstStaticPadTemplate * hsrc, GstStaticPadTemplate * hsink); +GST_EXPORT GstHarness * gst_harness_new (const gchar * element_name); +GST_EXPORT GstHarness * gst_harness_new_parse (const gchar * launchline); +GST_EXPORT void gst_harness_add_parse (GstHarness * h, const gchar * launchline); +GST_EXPORT void gst_harness_teardown (GstHarness * h); +GST_EXPORT void gst_harness_add_element_src_pad (GstHarness * h, GstPad * srcpad); +GST_EXPORT void gst_harness_add_element_sink_pad (GstHarness * h, GstPad * sinkpad); /* Caps Functions */ +GST_EXPORT void gst_harness_set_src_caps (GstHarness * h, GstCaps * caps); +GST_EXPORT void gst_harness_set_sink_caps (GstHarness * h, GstCaps * caps); +GST_EXPORT void gst_harness_set_caps (GstHarness * h, GstCaps * in, GstCaps * out); +GST_EXPORT void gst_harness_set_src_caps_str (GstHarness * h, const gchar * str); +GST_EXPORT void gst_harness_set_sink_caps_str (GstHarness * h, const gchar * str); +GST_EXPORT void gst_harness_set_caps_str (GstHarness * h, const gchar * in, const gchar * out); /* Clock Functions */ +GST_EXPORT void gst_harness_use_systemclock (GstHarness * h); +GST_EXPORT void gst_harness_use_testclock (GstHarness * h); +GST_EXPORT GstTestClock * gst_harness_get_testclock (GstHarness * h); +GST_EXPORT gboolean gst_harness_set_time (GstHarness * h, GstClockTime time); +GST_EXPORT gboolean gst_harness_wait_for_clock_id_waits (GstHarness * h, guint waits, guint timeout); +GST_EXPORT gboolean gst_harness_crank_single_clock_wait (GstHarness * h); +GST_EXPORT gboolean gst_harness_crank_multiple_clock_waits (GstHarness * h, guint waits); /* misc */ + +GST_EXPORT void gst_harness_play (GstHarness * h); +GST_EXPORT void gst_harness_set_blocking_push_mode (GstHarness * h); +GST_EXPORT void gst_harness_set_forwarding (GstHarness * h, gboolean forwarding); /* buffers */ +GST_EXPORT GstBuffer * gst_harness_create_buffer (GstHarness * h, gsize size); +GST_EXPORT GstFlowReturn gst_harness_push (GstHarness * h, GstBuffer * buffer); +GST_EXPORT GstBuffer * gst_harness_pull (GstHarness * h); +GST_EXPORT GstBuffer * gst_harness_try_pull (GstHarness * h); +GST_EXPORT GstBuffer * gst_harness_push_and_pull (GstHarness * h, GstBuffer * buffer); +GST_EXPORT guint gst_harness_buffers_received (GstHarness * h); +GST_EXPORT guint gst_harness_buffers_in_queue (GstHarness * h); +GST_EXPORT void gst_harness_set_drop_buffers (GstHarness * h, gboolean drop_buffers); +GST_EXPORT void gst_harness_dump_to_file (GstHarness * h, const gchar * filename); +GST_EXPORT GstClockTime gst_harness_get_last_pushed_timestamp (GstHarness * h); /* downstream events */ +GST_EXPORT gboolean gst_harness_push_event (GstHarness * h, GstEvent * event); +GST_EXPORT GstEvent * gst_harness_pull_event (GstHarness * h); +GST_EXPORT GstEvent * gst_harness_try_pull_event (GstHarness * h); +GST_EXPORT guint gst_harness_events_received (GstHarness * h); +GST_EXPORT guint gst_harness_events_in_queue (GstHarness * h); /* upstream events */ +GST_EXPORT gboolean gst_harness_push_upstream_event (GstHarness * h, GstEvent * event); +GST_EXPORT GstEvent * gst_harness_pull_upstream_event (GstHarness * h); +GST_EXPORT GstEvent * gst_harness_try_pull_upstream_event (GstHarness * h); +GST_EXPORT guint gst_harness_upstream_events_received (GstHarness * h); +GST_EXPORT guint gst_harness_upstream_events_in_queue (GstHarness * h); /* latency */ +GST_EXPORT GstClockTime gst_harness_query_latency (GstHarness * h); +GST_EXPORT void gst_harness_set_upstream_latency (GstHarness * h, GstClockTime latency); /* allocator and allocation params */ +GST_EXPORT void gst_harness_set_propose_allocator (GstHarness * h, GstAllocator * allocator, const GstAllocationParams * params); +GST_EXPORT void gst_harness_get_allocator (GstHarness * h, GstAllocator ** allocator, GstAllocationParams * params); /* src-harness */ +GST_EXPORT void gst_harness_add_src_harness (GstHarness * h, GstHarness * src_harness, gboolean has_clock_wait); +GST_EXPORT void gst_harness_add_src (GstHarness * h, const gchar * src_element_name, gboolean has_clock_wait); +GST_EXPORT void gst_harness_add_src_parse (GstHarness * h, const gchar * launchline, gboolean has_clock_wait); +GST_EXPORT GstFlowReturn gst_harness_push_from_src (GstHarness * h); +GST_EXPORT GstFlowReturn gst_harness_src_crank_and_push_many (GstHarness * h, gint cranks, gint pushes); +GST_EXPORT gboolean gst_harness_src_push_event (GstHarness * h); /* sink-harness */ +GST_EXPORT void gst_harness_add_sink_harness (GstHarness * h, GstHarness * sink_harness); +GST_EXPORT void gst_harness_add_sink (GstHarness * h, const gchar * sink_element_name); +GST_EXPORT void gst_harness_add_sink_parse (GstHarness * h, const gchar * launchline); +GST_EXPORT GstFlowReturn gst_harness_push_to_sink (GstHarness * h); +GST_EXPORT GstFlowReturn gst_harness_sink_push_many (GstHarness * h, gint pushes); /* convenience functions */ +GST_EXPORT GstElement * gst_harness_find_element (GstHarness * h, const gchar * element_name); +GST_EXPORT void gst_harness_set (GstHarness * h, const gchar * element_name, const gchar * first_property_name, ...); +GST_EXPORT void gst_harness_get (GstHarness * h, const gchar * element_name, const gchar * first_property_name, ...); +GST_EXPORT void gst_harness_add_probe (GstHarness * h, const gchar * element_name, const gchar * pad_name, @@ -266,8 +334,10 @@ void gst_harness_add_probe (GstHarness * h, /* Stress */ +GST_EXPORT guint gst_harness_stress_thread_stop (GstHarnessThread * t); +GST_EXPORT GstHarnessThread * gst_harness_stress_custom_start (GstHarness * h, GFunc init, GFunc callback, @@ -277,12 +347,14 @@ GstHarnessThread * gst_harness_stress_custom_start (GstHarness * h, #define gst_harness_stress_statechange_start(h) \ gst_harness_stress_statechange_start_full (h, G_USEC_PER_SEC / 100) +GST_EXPORT GstHarnessThread * gst_harness_stress_statechange_start_full (GstHarness * h, gulong sleep); #define gst_harness_stress_push_buffer_start(h, c, s, b) \ gst_harness_stress_push_buffer_start_full (h, c, s, b, 0) +GST_EXPORT GstHarnessThread * gst_harness_stress_push_buffer_start_full (GstHarness * h, GstCaps * caps, const GstSegment * segment, @@ -301,6 +373,7 @@ typedef GstBuffer * (*GstHarnessPrepareBufferFunc) (GstHarness * h, gpointer dat #define gst_harness_stress_push_buffer_with_cb_start(h, c, s, f, d, n) \ gst_harness_stress_push_buffer_with_cb_start_full (h, c, s, f, d, n, 0) +GST_EXPORT GstHarnessThread * gst_harness_stress_push_buffer_with_cb_start_full (GstHarness * h, GstCaps * caps, const GstSegment * segment, @@ -312,6 +385,7 @@ GstHarnessThread * gst_harness_stress_push_buffer_with_cb_start_full (GstHarness #define gst_harness_stress_push_event_start(h, e) \ gst_harness_stress_push_event_start_full (h, e, 0) +GST_EXPORT GstHarnessThread * gst_harness_stress_push_event_start_full (GstHarness * h, GstEvent * event, gulong sleep); @@ -328,6 +402,7 @@ typedef GstEvent * (*GstHarnessPrepareEventFunc) (GstHarness * h, gpointer data) #define gst_harness_stress_push_event_with_cb_start(h, f, d, n) \ gst_harness_stress_push_event_with_cb_start_full (h, f, d, n, 0) +GST_EXPORT GstHarnessThread * gst_harness_stress_push_event_with_cb_start_full (GstHarness * h, GstHarnessPrepareEventFunc func, gpointer data, @@ -337,6 +412,7 @@ GstHarnessThread * gst_harness_stress_push_event_with_cb_start_full (GstHarness #define gst_harness_stress_send_upstream_event_start(h, e) \ gst_harness_stress_push_upstream_event_start_full (h, e, 0) +GST_EXPORT GstHarnessThread * gst_harness_stress_push_upstream_event_start_full (GstHarness * h, GstEvent * event, gulong sleep); @@ -344,6 +420,7 @@ GstHarnessThread * gst_harness_stress_push_upstream_event_start_full (GstHarness #define gst_harness_stress_send_upstream_event_with_cb_start(h, f, d, n) \ gst_harness_stress_push_upstream_event_with_cb_start_full (h, f, d, n, 0) +GST_EXPORT GstHarnessThread * gst_harness_stress_push_upstream_event_with_cb_start_full (GstHarness * h, GstHarnessPrepareEventFunc func, gpointer data, @@ -354,6 +431,7 @@ GstHarnessThread * gst_harness_stress_push_upstream_event_with_cb_start_full (Gs #define gst_harness_stress_property_start(h, n, v) \ gst_harness_stress_property_start_full (h, n, v, G_USEC_PER_SEC / 1000) +GST_EXPORT GstHarnessThread * gst_harness_stress_property_start_full (GstHarness * h, const gchar * name, const GValue * value, @@ -362,6 +440,7 @@ GstHarnessThread * gst_harness_stress_property_start_full (GstHarness * h, #define gst_harness_stress_requestpad_start(h, t, n, c, r) \ gst_harness_stress_requestpad_start_full (h, t, n, c, r, G_USEC_PER_SEC / 100) +GST_EXPORT GstHarnessThread * gst_harness_stress_requestpad_start_full (GstHarness * h, GstPadTemplate * templ, const gchar * name, diff --git a/libs/gst/check/gsttestclock.c b/libs/gst/check/gsttestclock.c index d1d2c30..14ac669 100644 --- a/libs/gst/check/gsttestclock.c +++ b/libs/gst/check/gsttestclock.c @@ -911,10 +911,6 @@ gst_test_clock_wait_for_next_pending_id (GstTestClock * test_clock, * Deprecated: use gst_test_clock_wait_for_multiple_pending_ids() instead. */ #ifndef GST_REMOVE_DEPRECATED -#ifdef GST_DISABLE_DEPRECATED -void gst_test_clock_wait_for_pending_id_count (GstTestClock * test_clock, - guint count); -#endif void gst_test_clock_wait_for_pending_id_count (GstTestClock * test_clock, guint count) diff --git a/libs/gst/check/gsttestclock.h b/libs/gst/check/gsttestclock.h index 17505c2..5cf42c7 100644 --- a/libs/gst/check/gsttestclock.h +++ b/libs/gst/check/gsttestclock.h @@ -74,46 +74,60 @@ struct _GstTestClockClass GstClockClass parent_class; }; +GST_EXPORT GType gst_test_clock_get_type (void); +GST_EXPORT GstClock * gst_test_clock_new (void); +GST_EXPORT GstClock * gst_test_clock_new_with_start_time (GstClockTime start_time); +GST_EXPORT void gst_test_clock_set_time (GstTestClock * test_clock, GstClockTime new_time); +GST_EXPORT void gst_test_clock_advance_time (GstTestClock * test_clock, GstClockTimeDiff delta); +GST_EXPORT guint gst_test_clock_peek_id_count (GstTestClock * test_clock); +GST_EXPORT gboolean gst_test_clock_has_id (GstTestClock * test_clock, GstClockID id); +GST_EXPORT gboolean gst_test_clock_peek_next_pending_id (GstTestClock * test_clock, GstClockID * pending_id); +GST_EXPORT void gst_test_clock_wait_for_next_pending_id (GstTestClock * test_clock, GstClockID * pending_id); -#ifndef GST_DISABLE_DEPRECATED +GST_DEPRECATED_FOR(gst_test_clock_wait_for_multiple_pending_ids) void gst_test_clock_wait_for_pending_id_count (GstTestClock * test_clock, guint count); -#endif +GST_EXPORT GstClockID gst_test_clock_process_next_clock_id (GstTestClock * test_clock); +GST_EXPORT GstClockTime gst_test_clock_get_next_entry_time (GstTestClock * test_clock); +GST_EXPORT void gst_test_clock_wait_for_multiple_pending_ids (GstTestClock * test_clock, guint count, GList ** pending_list); +GST_EXPORT guint gst_test_clock_process_id_list (GstTestClock * test_clock, const GList * pending_list); +GST_EXPORT GstClockTime gst_test_clock_id_list_get_latest_time (const GList * pending_list); +GST_EXPORT gboolean gst_test_clock_crank (GstTestClock * test_clock); #ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC diff --git a/libs/gst/check/libcheck/check.h.in b/libs/gst/check/libcheck/check.h.in index 9da7fd5..fa7483c 100644 --- a/libs/gst/check/libcheck/check.h.in +++ b/libs/gst/check/libcheck/check.h.in @@ -65,15 +65,15 @@ CK_CPPSTART * on the command line. */ #ifndef CK_DLL_EXP -#define CK_DLL_EXP +#define CK_DLL_EXP extern #endif /* check version numbers */ #define CHECK_MAJOR_VERSION (@CHECK_MAJOR_VERSION@) #define CHECK_MINOR_VERSION (@CHECK_MINOR_VERSION@) #define CHECK_MICRO_VERSION (@CHECK_MICRO_VERSION@) -CK_DLL_EXP extern int CK_EXPORT check_major_version; -CK_DLL_EXP extern int CK_EXPORT check_minor_version; -CK_DLL_EXP extern int CK_EXPORT check_micro_version; +CK_DLL_EXP /*extern*/ int CK_EXPORT check_major_version; +CK_DLL_EXP /*extern*/ int CK_EXPORT check_minor_version; +CK_DLL_EXP /*extern*/ int CK_EXPORT check_micro_version; #ifndef NULL #define NULL ((void*)0) -- 2.7.4