[x265] [PATCH V2] api: introduce a less version strict API query
Steve Borho
steve at borho.org
Mon May 18 18:00:37 CEST 2015
# HG changeset patch
# User Steve Borho <steve at borho.org>
# Date 1431625954 18000
# Thu May 14 12:52:34 2015 -0500
# Node ID 5cb5ede658768f3e18514af807b0bd4e3ee63a01
# Parent 6511d96ee057a8f0fe8a3cfcd9f327c8966f2ae2
api: introduce a less version strict API query
The intention is to allow applications to use libx265 libraries with different
X265_BUILD numbers than the x265.h header they were compiled with by keeping
a little more information about the nature of each API bump.
This should have no effect on existing applications, X265_BUILD will still be
incremented each time the public API is changed, but applications which use
this new x265_get_api_build() method will be able to dlopen() and use any
version of libx265 that returns an API pointer that passes validation checks:
1. check api->api_major_version == X265_MAJOR_VERSION
2. check api->sizeof_param == sizeof(x265_param) if param is dereferenced
3. check api->sizeof_picture == sizeof(x265_picture)
4. check api->sizeof_analysis_data ..
etc.
apps that use param_alloc()/param_free()/param_parse() can skip step 2 and
thus ignore the primary cause of most X265_BUILD bumps.
The only additional work for x265 developers is to increment X265_MAJOR_VERSION
when warranted (which should hopefully be very rarely).
Since this commit is modifying x265_api, we take the opportunity to rename
max_bit_depth to the more accurate bit_depth, since each API will only be
capable of encoding at a single bit depth.
diff -r 6511d96ee057 -r 5cb5ede65876 doc/reST/api.rst
--- a/doc/reST/api.rst Mon May 18 10:07:04 2015 -0500
+++ b/doc/reST/api.rst Thu May 14 12:52:34 2015 -0500
@@ -419,3 +419,57 @@
and then also install libx265_main10.so (symlinked to its numbered solib).
Thus applications which use x265_api_get() will be able to generate main
or main10 bitstreams.
+
+There is a second bit-depth introspection method that is designed for
+applications which need more flexibility in API versioning. If you use
+the public API described at the top of this page or x265_api_get() then
+your application must be recompiled each time x265 changes its public
+API and bumps its build number (X265_BUILD, which is also the SONAME on
+POSIX systems). But if you use **x265_api_query** and dynamically link to
+libx265 (use dlopen() on POSIX or LoadLibrary() on Windows) your
+application is no longer directly tied to the API version of x265.h that
+it was compiled against.
+
+ /* x265_api_query:
+ * Retrieve the programming interface for a linked x265 library, like
+ * x265_api_get(), except this function accepts X265_BUILD as the second
+ * argument rather than using the build number as part of the function name.
+ * Applications which dynamically link to libx265 can use this interface to
+ * query the library API and achieve a relative amount of version skew
+ * flexibility. The function may return NULL if the library determines that
+ * the apiVersion that your application was compiled against is not compatible
+ * with the library you have linked with.
+ *
+ * api_major_version will be incremented any time non-backward compatible
+ * changes are made to any public structures or functions. If
+ * api_major_version does not match X265_MAJOR_VERSION from the x265.h your
+ * application compiled against, your application must not use the returned
+ * x265_api pointer.
+ *
+ * Users of this API *must* also validate the sizes of any structures which
+ * are not treated as opaque in application code. For instance, if your
+ * application dereferences a x265_param pointer, then it must check that
+ * api->sizeof_param matches the sizeof(x265_param) that your application
+ * compiled with. */
+ const x265_api* x265_api_query(int bitDepth, int apiVersion, int* err);
+
+A number of validations must be performed on the returned API structure
+in order to determine if it is safe for use by your application. If you
+do not perform these checks, your application is liable to crash.
+
+ if (api->api_major_version != X265_MAJOR_VERSION) /* do not use */
+ if (api->sizeof_param != sizeof(x265_param)) /* do not use */
+ if (api->sizeof_picture != sizeof(x265_picture)) /* do not use */
+ if (api->sizeof_stats != sizeof(x265_stats)) /* do not use */
+ if (api->sizeof_zone != sizeof(x265_zone)) /* do not use */
+ etc.
+
+Note that if your application does not directly allocate or dereference
+one of these structures, if it treats the structure as opaque or does
+not use it at all, then it can skip the size check for that structure.
+
+In particular, if your application uses api->param_alloc(),
+api->param_free(), api->param_parse(), etc and never directly accesses
+any x265_param fields, then it can skip the check on the
+sizeof(x265_parm) and thereby ignore changes to that structure (which
+account for a large percentage of X265_BUILD bumps).
diff -r 6511d96ee057 -r 5cb5ede65876 source/CMakeLists.txt
--- a/source/CMakeLists.txt Mon May 18 10:07:04 2015 -0500
+++ b/source/CMakeLists.txt Thu May 14 12:52:34 2015 -0500
@@ -30,7 +30,7 @@
mark_as_advanced(FPROFILE_USE FPROFILE_GENERATE NATIVE_BUILD)
# X265_BUILD must be incremented each time the public API is changed
-set(X265_BUILD 59)
+set(X265_BUILD 60)
configure_file("${PROJECT_SOURCE_DIR}/x265.def.in"
"${PROJECT_BINARY_DIR}/x265.def")
configure_file("${PROJECT_SOURCE_DIR}/x265_config.h.in"
diff -r 6511d96ee057 -r 5cb5ede65876 source/encoder/api.cpp
--- a/source/encoder/api.cpp Mon May 18 10:07:04 2015 -0500
+++ b/source/encoder/api.cpp Thu May 14 12:52:34 2015 -0500
@@ -253,6 +253,18 @@
static const x265_api libapi =
{
+ X265_MAJOR_VERSION,
+ X265_BUILD,
+ sizeof(x265_param),
+ sizeof(x265_picture),
+ sizeof(x265_analysis_data),
+ sizeof(x265_zone),
+ sizeof(x265_stats),
+
+ x265_max_bit_depth,
+ x265_version_str,
+ x265_build_info_str,
+
&x265_param_alloc,
&x265_param_free,
&x265_param_default,
@@ -271,9 +283,6 @@
&x265_encoder_log,
&x265_encoder_close,
&x265_cleanup,
- x265_version_str,
- x265_build_info_str,
- x265_max_bit_depth,
};
typedef const x265_api* (*api_get_func)(int bitDepth);
@@ -328,7 +337,7 @@
}
#endif
- if (api && bitDepth != api->max_bit_depth)
+ if (api && bitDepth != api->bit_depth)
{
x265_log(NULL, X265_LOG_WARNING, "%s does not support requested bitDepth %d\n", libname, bitDepth);
return NULL;
@@ -339,3 +348,67 @@
return &libapi;
}
+
+const x265_api* x265_api_query(int bitDepth, int apiVersion, int* err)
+{
+ if (apiVersion < 51)
+ {
+ /* builds before 1.6 had re-ordered public structs */
+ if (err) *err = X265_API_QUERY_ERR_VER_REFUSED;
+ return NULL;
+ }
+
+ if (bitDepth && bitDepth != X265_DEPTH)
+ {
+ const char* libname = NULL;
+ const char* method = "x265_api_query";
+
+ if (bitDepth == 12)
+ libname = "libx265_main12" ext;
+ else if (bitDepth == 10)
+ libname = "libx265_main10" ext;
+ else if (bitDepth == 8)
+ libname = "libx265_main" ext;
+ else
+ {
+ if (err) *err = X265_API_QUERY_ERR_LIB_NOT_FOUND;
+ return NULL;
+ }
+
+ const x265_api* api = NULL;
+ int e = X265_API_QUERY_ERR_LIB_NOT_FOUND;
+
+#if _WIN32
+ HMODULE h = LoadLibraryA(libname);
+ if (h)
+ {
+ e = X265_API_QUERY_ERR_FUNC_NOT_FOUND;
+ api_get_func get = (api_get_func)GetProcAddress(h, method);
+ if (get)
+ api = get(0);
+ }
+#else
+ void* h = dlopen(libname, RTLD_LAZY | RTLD_LOCAL);
+ if (h)
+ {
+ e = X265_API_QUERY_ERR_FUNC_NOT_FOUND;
+ api_get_func get = (api_get_func)dlsym(h, method);
+ if (get)
+ api = get(0);
+ }
+#endif
+
+ if (api && bitDepth != api->bit_depth)
+ {
+ x265_log(NULL, X265_LOG_WARNING, "%s does not support requested bitDepth %d\n", libname, bitDepth);
+ if (err) *err = X265_API_QUERY_ERR_WRONG_BITDEPTH;
+ return NULL;
+ }
+
+ if (err) *err = api ? X265_API_QUERY_ERR_NONE : e;
+ return api;
+ }
+
+ if (err) *err = X265_API_QUERY_ERR_NONE;
+ return &libapi;
+}
diff -r 6511d96ee057 -r 5cb5ede65876 source/x265.cpp
--- a/source/x265.cpp Mon May 18 10:07:04 2015 -0500
+++ b/source/x265.cpp Thu May 14 12:52:34 2015 -0500
@@ -318,9 +318,9 @@
return true;
}
- if (param->internalBitDepth != api->max_bit_depth)
+ if (param->internalBitDepth != api->bit_depth)
{
- x265_log(param, X265_LOG_ERROR, "Only bit depths of %d are supported in this build\n", api->max_bit_depth);
+ x265_log(param, X265_LOG_ERROR, "Only bit depths of %d are supported in this build\n", api->bit_depth);
return true;
}
diff -r 6511d96ee057 -r 5cb5ede65876 source/x265.def.in
--- a/source/x265.def.in Mon May 18 10:07:04 2015 -0500
+++ b/source/x265.def.in Thu May 14 12:52:34 2015 -0500
@@ -21,3 +21,4 @@
x265_encoder_close
x265_cleanup
x265_api_get_${X265_BUILD}
+x265_api_query
diff -r 6511d96ee057 -r 5cb5ede65876 source/x265.h
--- a/source/x265.h Mon May 18 10:07:04 2015 -0500
+++ b/source/x265.h Thu May 14 12:52:34 2015 -0500
@@ -1278,15 +1278,28 @@
* release library static allocations, reset configured CTU size */
void x265_cleanup(void);
+#define X265_MAJOR_VERSION 1
/* === Multi-lib API ===
- * By using this method to gain access to the libx265 interfaces, you allow shim
- * implementations of x265_api_get() to choose between various available libx265
- * libraries based on the encoder parameters. The most likely use case is to
- * choose between 8bpp and 16bpp builds of libx265. */
+ * By using this method to gain access to the libx265 interfaces, you allow run-
+ * time selection between various available libx265 libraries based on the
+ * encoder parameters. The most likely use case is to choose between 8bpp and
+ * 16bpp builds of libx265. */
typedef struct x265_api
{
+ int api_major_version; /* X265_MAJOR_VERSION */
+ int api_build_number; /* X265_BUILD (soname) */
+ int sizeof_param; /* sizeof(x265_param) */
+ int sizeof_picture; /* sizeof(x265_picture) */
+ int sizeof_analysis_data; /* sizeof(x265_analysis_data) */
+ int sizeof_zone; /* sizeof(x265_zone) */
+ int sizeof_stats; /* sizeof(x265_stats) */
+
+ int bit_depth;
+ const char* version_str;
+ const char* build_info_str;
+
/* libx265 public API functions, documented above with x265_ prefixes */
x265_param* (*param_alloc)(void);
void (*param_free)(x265_param*);
@@ -1306,9 +1319,7 @@
void (*encoder_log)(x265_encoder*, int, char**);
void (*encoder_close)(x265_encoder*);
void (*cleanup)(void);
- const char* version_str;
- const char* build_info_str;
- int max_bit_depth;
+ /* add new pointers to the end, or increment X265_MAJOR_VERSION */
} x265_api;
/* Force a link error in the case of linking against an incompatible API version.
@@ -1332,6 +1343,43 @@
* Obviously the shared library file extension is platform specific */
const x265_api* x265_api_get(int bitDepth);
+/* x265_api_query:
+ * Retrieve the programming interface for a linked x265 library, like
+ * x265_api_get(), except this function accepts X265_BUILD as the second
+ * argument rather than using the build number as part of the function name.
+ * Applications which dynamically link to libx265 can use this interface to
+ * query the library API and achieve a relative amount of version skew
+ * flexibility. The function may return NULL if the library determines that
+ * the apiVersion that your application was compiled against is not compatible
+ * with the library you have linked with.
+ *
+ * api_major_version will be incremented any time non-backward compatible
+ * changes are made to any public structures or functions. If
+ * api_major_version does not match X265_MAJOR_VERSION from the x265.h your
+ * application compiled against, your application must not use the returned
+ * x265_api pointer.
+ *
+ * Users of this API *must* also validate the sizes of any structures which
+ * are not treated as opaque in application code. For instance, if your
+ * application dereferences a x265_param pointer, then it must check that
+ * api->sizeof_param matches the sizeof(x265_param) that your application
+ * compiled with. */
+const x265_api* x265_api_query(int bitDepth, int apiVersion, int* err);
+
+#define X265_API_QUERY_ERR_NONE 0 /* returned API pointer is non-NULL */
+#define X265_API_QUERY_ERR_VER_REFUSED 1 /* incompatible version skew */
+#define X265_API_QUERY_ERR_LIB_NOT_FOUND 2 /* libx265_main10 not found, for ex */
+#define X265_API_QUERY_ERR_FUNC_NOT_FOUND 3 /* unable to bind x265_api_query */
+#define X265_API_QUERY_ERR_WRONG_BITDEPTH 4 /* libx265_main10 not 10bit, for ex */
+
+static const char * const x265_api_query_errnames[] = {
+ "api queried from libx265",
+ "libx265 version is not compatible with this application",
+ "unable to bind a libx265 with requested bit depth",
+ "unable to bind x265_api_query from libx265",
+ "libx265 has an invalid bitdepth"
+};
+
#ifdef __cplusplus
}
#endif
More information about the x265-devel
mailing list