diff --git a/setup.py b/setup.py index b071fb27b3e22ea35ad79f486a02dde3477b74e1..42382872744bd811337e1c31975c64b2b1f5f4a8 100644 --- a/setup.py +++ b/setup.py @@ -97,10 +97,23 @@ setup( ], ext_modules = [ + Extension("xbob.io._externals", + [ + "xbob/io/externals.cpp", + ], + define_macros=define_macros, + include_dirs=include_dirs, + extra_compile_args=extra_compile_args, + library_dirs=bob_pkg.library_directories(), + runtime_library_dirs=bob_pkg.library_directories(), + libraries=bob_libraries, + language="c++", + ), Extension("xbob.io._library", [ "xbob/io/bobskin.cpp", "xbob/io/file.cpp", + "xbob/io/videoreader.cpp", "xbob/io/main.cpp", ], define_macros=define_macros, diff --git a/xbob/io/__init__.py b/xbob/io/__init__.py index 8a062569adcbcb8df36e57fdb9a2f4071d622653..39f2bbefcc430590280ab787beb10b75fbf0a36d 100644 --- a/xbob/io/__init__.py +++ b/xbob/io/__init__.py @@ -1,4 +1,5 @@ from ._library import __version__, __api_version__, File +from . import _externals import os @@ -146,28 +147,28 @@ def peek(filename): Effectively, this is the same as creating a :py:class:`bob.io.File` object with the mode flag set to `r` (read-only) and returning - :py:attr:`bob.io.File.type`. + :py:attr:`bob.io.File.describe()`. Parameters: filename The name of the file to peek information from """ - return File(filename, 'r').type + return File(filename, 'r').describe() def peek_all(filename): """Returns the type of array (for full readouts) saved in the given file. Effectively, this is the same as creating a :py:class:`bob.io.File` object with the mode flag set to `r` (read-only) and returning - :py:attr:`bob.io.File.type_all`. + :py:attr:`bob.io.File.describe(all=True)`. Parameters: filename The name of the file to peek information from """ - return File(filename, 'r').type_all + return File(filename, 'r').describe(all=True) # Keeps compatibility with the previously existing API open = File diff --git a/xbob/io/externals.cpp b/xbob/io/externals.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e474aaaa089a5969070dd590650c25a4fd876f8e --- /dev/null +++ b/xbob/io/externals.cpp @@ -0,0 +1,846 @@ +/** + * @author Andre Anjos <andre.anjos@idiap.ch> + * @date Thu 7 Nov 13:50:16 2013 + * + * @brief Binds configuration information available from bob + */ + +#define XBOB_IO_MODULE +#include <xbob.io/config.h> + +#define XBOB_IO_VERSIONS_MODULE_NAME _externals + +#include <string> +#include <cstdlib> +#include <boost/preprocessor/stringize.hpp> +#include <boost/format.hpp> + +#include <bob/config.h> +#include <bob/io/CodecRegistry.h> +#include <bob/io/VideoUtilities.h> + +extern "C" { + +#include <Python.h> + +#ifdef NO_IMPORT_ARRAY +#undef NO_IMPORT_ARRAY +#endif +#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION +#include <numpy/arrayobject.h> + +#include <hdf5.h> +#include <jpeglib.h> + +#define PNG_SKIP_SETJMP_CHECK +// #define requires because of the problematic pngconf.h. +// Look at the thread here: +// https://bugs.launchpad.net/ubuntu/+source/libpng/+bug/218409 +#include <png.h> + +#if WITH_FFMPEG +# include <libavformat/avformat.h> +# include <libavcodec/avcodec.h> +# include <libavutil/avutil.h> +# include <libswscale/swscale.h> +# include <libavutil/opt.h> +# include <libavutil/pixdesc.h> +#endif + +#include <gif_lib.h> + +#if WITH_MATIO +#include <matio.h> +#endif + +#include <tiffio.h> + +} + +static int dict_set(PyObject* d, const char* key, const char* value) { + PyObject* v = Py_BuildValue("s", value); + if (!v) return 0; + int retval = PyDict_SetItemString(d, key, v); + Py_DECREF(v); + if (retval == 0) return 1; //all good + return 0; //a problem occurred +} + +static int dict_steal(PyObject* d, const char* key, PyObject* value) { + if (!value) return 0; + int retval = PyDict_SetItemString(d, key, value); + Py_DECREF(value); + if (retval == 0) return 1; //all good + return 0; //a problem occurred +} + +/** + * Creates an str object, from a C or C++ string. Returns a **new + * reference**. + */ +static PyObject* make_object(const char* s) { + return Py_BuildValue("s", s); +} + +#if WITH_FFMPEG + +static PyObject* make_object(bool v) { + if (v) Py_RETURN_TRUE; + Py_RETURN_FALSE; +} + +static PyObject* make_object(unsigned int v) { + return Py_BuildValue("n", v); +} + +static PyObject* make_object(double v) { + return PyFloat_FromDouble(v); +} + +static PyObject* make_object(PyObject* v) { + Py_INCREF(v); + return v; +} + +/** + * Sets a dictionary entry using a string as key and another one as value. + * Returns 1 in case of success, 0 in case of failure. + */ +template <typename T> +int dict_set_string(boost::shared_ptr<PyObject> d, const char* key, T value) { + PyObject* pyvalue = make_object(value); + if (!pyvalue) return 0; + int retval = PyDict_SetItemString(d.get(), key, pyvalue); + Py_DECREF(pyvalue); + if (retval == 0) return 1; //all good + return 0; //a problem occurred +} + +/** + * Sets a dictionary entry using a string as key and another one as value. + * Returns 1 in case of success, 0 in case of faiulre. + */ +template <typename T> +int list_append(boost::shared_ptr<PyObject> l, T value) { + PyObject* pyvalue = make_object(value); + if (!pyvalue) return 0; + int retval = PyList_Append(l.get(), pyvalue); + Py_DECREF(pyvalue); + if (retval == 0) return 1; //all good + return 0; //a problem occurred +} + +/** + * A deleter, for shared_ptr's + */ +void pyobject_deleter(PyObject* o) { + Py_XDECREF(o); +} + +/** + * Checks if it is a Python string for Python 2.x or 3.x + */ +int check_string(PyObject* o) { +# if PY_VERSION_HEX >= 0x03000000 + return PyUnicode_Check(o); +# else + return PyString_Check(o); +# endif +} + +#endif /* WITH_FFMPEG */ + +/*********************************************************** + * Version number generation + ***********************************************************/ + +static PyObject* hdf5_version() { + boost::format f("%s.%s.%s"); + f % BOOST_PP_STRINGIZE(H5_VERS_MAJOR); + f % BOOST_PP_STRINGIZE(H5_VERS_MINOR); + f % BOOST_PP_STRINGIZE(H5_VERS_RELEASE); + return Py_BuildValue("s", f.str().c_str()); +} + +/** + * FFmpeg version + */ +static PyObject* ffmpeg_version() { + PyObject* retval = PyDict_New(); + if (!retval) return 0; + +#if WITH_FFMPEG +# if defined(FFMPEG_VERSION) + if (std::strlen(FFMPEG_VERSION)) { + if (!dict_set(retval, "ffmpeg", FFMPEG_VERSION)) { + Py_DECREF(retval); + return 0; + } + } +# endif + if (!dict_set(retval, "avformat", BOOST_PP_STRINGIZE(LIBAVFORMAT_VERSION))) { + Py_DECREF(retval); + return 0; + } + if (!dict_set(retval, "avcodec", BOOST_PP_STRINGIZE(LIBAVCODEC_VERSION))) { + Py_DECREF(retval); + return 0; + } + if (!dict_set(retval, "avutil", BOOST_PP_STRINGIZE(LIBAVUTIL_VERSION))) { + Py_DECREF(retval); + return 0; + } + if (!dict_set(retval, "swscale", BOOST_PP_STRINGIZE(LIBSWSCALE_VERSION))) { + Py_DECREF(retval); + return 0; + } +#else + if (!dict_set(retval, "ffmpeg", "unavailable")) { + Py_DECREF(retval); + return 0; + } +#endif + return retval; +} + +/** + * LibJPEG version + */ +static PyObject* libjpeg_version() { + boost::format f("%d (compiled with %d bits depth)"); + f % JPEG_LIB_VERSION; + f % BITS_IN_JSAMPLE; + return Py_BuildValue("s", f.str().c_str()); +} + +/** + * Libpng version + */ +static PyObject* libpng_version() { + return Py_BuildValue("s", PNG_LIBPNG_VER_STRING); +} + +/** + * Libtiff version + */ +static PyObject* libtiff_version() { + + static const std::string beg_str("LIBTIFF, Version "); + static const size_t beg_len = beg_str.size(); + std::string vtiff(TIFFGetVersion()); + + // Remove first part if it starts with "LIBTIFF, Version " + if(vtiff.compare(0, beg_len, beg_str) == 0) + vtiff = vtiff.substr(beg_len); + + // Remove multiple (copyright) lines if any + size_t end_line = vtiff.find("\n"); + if(end_line != std::string::npos) + vtiff = vtiff.substr(0,end_line); + + return Py_BuildValue("s", vtiff.c_str()); + +} + +/** + * Version of giflib support + */ +static PyObject* giflib_version() { +#ifdef GIF_LIB_VERSION + return Py_BuildValue("s", GIF_LIB_VERSION); +#else + boost::format f("%s.%s.%s"); + f % BOOST_PP_STRINGIZE(GIFLIB_MAJOR); + f % BOOST_PP_STRINGIZE(GIFLIB_MINOR); + f % BOOST_PP_STRINGIZE(GIFLIB_RELEASE); + return Py_BuildValue("s", f.str().c_str()); +#endif +} + + +/** + * Matio, if compiled with such support + */ +static PyObject* matio_version() { +#if WITH_MATIO + boost::format f("%s.%s.%s"); + f % BOOST_PP_STRINGIZE(MATIO_MAJOR_VERSION); + f % BOOST_PP_STRINGIZE(MATIO_MINOR_VERSION); + f % BOOST_PP_STRINGIZE(MATIO_RELEASE_LEVEL); + return Py_BuildValue("s", f.str().c_str()); +#else + return Py_BuildValue("s", "unavailable"); +#endif +} + +static PyObject* build_version_dictionary() { + + PyObject* retval = PyDict_New(); + if (!retval) return 0; + + if (!dict_steal(retval, "HDF5", hdf5_version())) { + Py_DECREF(retval); + return 0; + } + + if (!dict_steal(retval, "FFmpeg", ffmpeg_version())) { + Py_DECREF(retval); + return 0; + } + + if (!dict_steal(retval, "libjpeg", libjpeg_version())) { + Py_DECREF(retval); + return 0; + } + + if (!dict_set(retval, "libnetpbm", "Unknown version")) { + Py_DECREF(retval); + return 0; + } + + if (!dict_steal(retval, "libpng", libpng_version())) { + Py_DECREF(retval); + return 0; + } + + if (!dict_steal(retval, "libtiff", libtiff_version())) { + Py_DECREF(retval); + return 0; + } + + if (!dict_steal(retval, "giflib", giflib_version())) { + Py_DECREF(retval); + return 0; + } + + if (!dict_steal(retval, "MatIO", matio_version())) { + Py_DECREF(retval); + return 0; + } + + return retval; +} + +/*********************************************************** + * FFmpeg information + ***********************************************************/ + +#if WITH_FFMPEG + +/** + * Describes a given codec. We return a **new reference** to a dictionary + * containing the codec properties. + */ +static PyObject* describe_codec(const AVCodec* codec) { + + /** + * We wrap the returned object into a smart pointer until we + * are absolutely sure all went good. At this point, we free + * the PyObject* from the boost encapsulation and return it. + */ + boost::shared_ptr<PyObject> retval(PyDict_New(), &pyobject_deleter); + if (!retval) return 0; + + /* Sets basic properties for the codec */ + if (!dict_set_string(retval, "name", codec->name)) return 0; + if (!dict_set_string(retval, "long_name", codec->long_name)) return 0; + if (!dict_set_string(retval, "id", (unsigned int)codec->id)) return 0; + + /** + * If pixel formats are available, creates and attaches a + * list with all their names + */ + + boost::shared_ptr<PyObject> pixfmt; + if (codec->pix_fmts) { + pixfmt.reset(PyList_New(0), &pyobject_deleter); + if (!pixfmt) return 0; + + unsigned int i=0; + while(codec->pix_fmts[i] != -1) { + if (!list_append(pixfmt, +#if LIBAVUTIL_VERSION_INT >= 0x320f01 //50.15.1 @ ffmpeg-0.6 + av_get_pix_fmt_name +#else + avcodec_get_pix_fmt_name +#endif + (codec->pix_fmts[i++]))) return 0; + } + pixfmt.reset(PySequence_Tuple(pixfmt.get()), &pyobject_deleter); + } + else { + Py_INCREF(Py_None); + pixfmt.reset(Py_None, &pyobject_deleter); + } + + if (!dict_set_string(retval, "pixfmts", pixfmt.get())) return 0; + + /* Get specific framerates for the codec, if any */ + const AVRational* rate = codec->supported_framerates; + boost::shared_ptr<PyObject> rates(PyList_New(0), &pyobject_deleter); + if (!rates) return 0; + + while (rate && rate->num && rate->den) { + list_append(rates, ((double)rate->num)/((double)rate->den)); + ++rate; + } + rates.reset(PySequence_Tuple(rates.get()), &pyobject_deleter); + if (!rates) return 0; + + if (!dict_set_string(retval, "specific_framerates_hz", rates.get())) return 0; + + /* Other codec capabilities */ +# ifdef CODEC_CAP_LOSSLESS + if (!dict_set_string(retval, "lossless", (bool)(codec->capabilities & CODEC_CAP_LOSSLESS))) return 0; +# endif +# ifdef CODEC_CAP_EXPERIMENTAL + if (!dict_set_string(retval, "experimental", (bool)(codec->capabilities & CODEC_CAP_EXPERIMENTAL))) return 0; +# endif +# ifdef CODEC_CAP_DELAY + if (!dict_set_string(retval, "delay", (bool)(codec->capabilities & CODEC_CAP_DELAY))) return 0; +# endif +# ifdef CODEC_CAP_HWACCEL + if (!dict_set_string(retval, "hardware_accelerated", (bool)(codec->capabilities & CODEC_CAP_HWACCEL))) return 0; +# endif + if (!dict_set_string(retval, "encode", (bool)(avcodec_find_encoder(codec->id)))) return 0; + if (!dict_set_string(retval, "decode", (bool)(avcodec_find_decoder(codec->id)))) return 0; + + /* If all went OK, detach the returned value from the smart pointer **/ + Py_INCREF(retval.get()); + return retval.get(); + +} + +/** + * Describes a given codec or raises, in case the codec cannot be accessed + */ +static PyObject* PyBobIo_DescribeEncoder(PyObject*, PyObject *args, PyObject* kwds) { + + /* Parses input arguments in a single shot */ + static const char* const_kwlist[] = {"key", 0}; + static char** kwlist = const_cast<char**>(const_kwlist); + + PyObject* key = 0; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &key)) return 0; + + if (!PyNumber_Check(key) && !check_string(key)) { + PyErr_SetString(PyExc_TypeError, "input `key' must be a number identifier or a string with the codec name"); + return 0; + } + + if (PyNumber_Check(key)) { + + /* If you get to this point, the user passed a number - re-parse */ + int id = 0; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, &id)) return 0; + + AVCodec* codec = avcodec_find_encoder((AVCodecID)id); + if (!codec) { + PyErr_Format(PyExc_RuntimeError, "ffmpeg::avcodec_find_encoder(%d == 0x%x) did not return a valid codec", id, id); + return 0; + } + + return describe_codec(codec); + } + + /* If you get to this point, the user passed a string - re-parse */ + const char* name = 0; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &name)) return 0; + + AVCodec* codec = avcodec_find_encoder_by_name(name); + if (!codec) { + PyErr_Format(PyExc_RuntimeError, "ffmpeg::avcodec_find_encoder_by_name(`%s') did not return a valid codec", name); + return 0; + } + + return describe_codec(codec); +} + +PyDoc_STRVAR(s_describe_encoder_str, "describe_encoder"); +PyDoc_STRVAR(s_describe_encoder_doc, +"describe_encoder([key]) -> dict\n\ +\n\ +Parameters:\n\ +\n\ +key\n\ + [int|str, optional] A key which can be either the encoder\n\ + name or its numerical identifier.\n\ +\n\ +Returns a dictionary containing a description of properties in\n\ +the given encoder.\n\ +"); + +/** + * Describes a given codec or raises, in case the codec cannot be accessed + */ +static PyObject* PyBobIo_DescribeDecoder(PyObject*, PyObject *args, PyObject* kwds) { + + /* Parses input arguments in a single shot */ + static const char* const_kwlist[] = {"key", 0}; + static char** kwlist = const_cast<char**>(const_kwlist); + + PyObject* key = 0; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &key)) return 0; + + if (!PyNumber_Check(key) && !check_string(key)) { + PyErr_SetString(PyExc_TypeError, "input `key' must be a number identifier or a string with the codec name"); + return 0; + } + + if (PyNumber_Check(key)) { + + /* If you get to this point, the user passed a number - re-parse */ + int id = 0; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, &id)) return 0; + + AVCodec* codec = avcodec_find_decoder((AVCodecID)id); + if (!codec) { + PyErr_Format(PyExc_RuntimeError, "ffmpeg::avcodec_find_decoder(%d == 0x%x) did not return a valid codec", id, id); + return 0; + } + + return describe_codec(codec); + } + + /* If you get to this point, the user passed a string - re-parse */ + const char* name = 0; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &name)) return 0; + + AVCodec* codec = avcodec_find_decoder_by_name(name); + if (!codec) { + PyErr_Format(PyExc_RuntimeError, "ffmpeg::avcodec_find_decoder_by_name(`%s') did not return a valid codec", name); + return 0; + } + + return describe_codec(codec); +} + +PyDoc_STRVAR(s_describe_decoder_str, "describe_decoder"); +PyDoc_STRVAR(s_describe_decoder_doc, +"describe_decoder([key]) -> dict\n\ +\n\ +Parameters:\n\ +\n\ +key\n\ + [int|str, optional] A key which can be either the decoder\n\ + name or its numerical identifier.\n\ +\n\ +Returns a dictionary containing a description of properties in\n\ +the given decoder.\n\ +"); + +static PyObject* PyBobIo_SupportedCodecs(PyObject*) { + + std::map<std::string, const AVCodec*> m; + bob::io::detail::ffmpeg::codecs_supported(m); + + PyObject* retval = PyDict_New(); + if (!retval) return 0; + + for (auto k=m.begin(); k!=m.end(); ++k) { + PyObject* pyvalue = describe_codec(k->second); + if (!pyvalue) { + Py_DECREF(retval); + return 0; + } + if (PyDict_SetItemString(retval, k->first.c_str(), pyvalue) != 0) { + Py_DECREF(pyvalue); + Py_DECREF(retval); + return 0; + } + Py_DECREF(pyvalue); + } + + return retval; + +} + +PyDoc_STRVAR(s_supported_codecs_str, "supported_video_codecs"); +PyDoc_STRVAR(s_supported_codecs_doc, +"supported_video_codecs() -> dict\n\ +\n\ +Returns a dictionary with currently supported video codec properties.\n\ +\n\ +Returns a dictionary containing a detailed description of the\n\ +built-in codecs for videos that are fully supported.\n\ +"); + +static PyObject* PyBobIo_AvailableCodecs(PyObject*) { + + std::map<std::string, const AVCodec*> m; + bob::io::detail::ffmpeg::codecs_installed(m); + + PyObject* retval = PyDict_New(); + if (!retval) return 0; + + for (auto k=m.begin(); k!=m.end(); ++k) { + PyObject* pyvalue = describe_codec(k->second); + if (!pyvalue) { + Py_DECREF(retval); + return 0; + } + if (PyDict_SetItemString(retval, k->first.c_str(), pyvalue) != 0) { + Py_DECREF(pyvalue); + Py_DECREF(retval); + return 0; + } + Py_DECREF(pyvalue); + } + + return retval; + +} + +PyDoc_STRVAR(s_available_codecs_str, "available_video_codecs"); +PyDoc_STRVAR(s_available_codecs_doc, +"available_video_codecs() -> dict\n\ +\n\ +Returns a dictionary with all available video codec properties.\n\ +\n\ +Returns a dictionary containing a detailed description of the\n\ +built-in codecs for videos that are available but **not necessarily\n\ +supported**.\n\ +"); + +/** + * Returns all input formats supported, related codecs and extensions + */ +/** +static dict supported_iformat_dictionary() { + std::map<std::string, AVInputFormat*> m; + bob::io::detail::ffmpeg::iformats_supported(m); + dict retval; + + for (auto k=m.begin(); k!=m.end(); ++k) { + dict property; + property["name"] = k->second->name; + property["long_name"] = k->second->long_name; + + // get extensions + std::vector<std::string> exts; + bob::io::detail::ffmpeg::tokenize_csv(k->second->extensions, exts); + list ext_list; + for (auto ext=exts.begin(); ext!=exts.end(); ++ext) ext_list.append(*ext); + property["extensions"] = tuple(ext_list); + + retval[k->first] = property; + } + + return retval; +} + +static dict available_iformat_dictionary() { + std::map<std::string, AVInputFormat*> m; + bob::io::detail::ffmpeg::iformats_installed(m); + dict retval; + + for (auto k=m.begin(); k!=m.end(); ++k) { + dict property; + property["name"] = k->second->name; + property["long_name"] = k->second->long_name; + + // get extensions + std::vector<std::string> exts; + bob::io::detail::ffmpeg::tokenize_csv(k->second->extensions, exts); + list ext_list; + for (auto ext=exts.begin(); ext!=exts.end(); ++ext) ext_list.append(*ext); + property["extensions"] = tuple(ext_list); + + retval[k->first] = property; + } + + return retval; +} +**/ + +/** + * Returns all output formats supported, related codecs and extensions + */ +/** +static dict supported_oformat_dictionary() { + std::map<std::string, AVOutputFormat*> m; + bob::io::detail::ffmpeg::oformats_supported(m); + dict retval; + + for (auto k=m.begin(); k!=m.end(); ++k) { + dict property; + property["name"] = k->second->name; + property["long_name"] = k->second->long_name; + property["mime_type"] = k->second->mime_type; + + // get extensions + std::vector<std::string> exts; + bob::io::detail::ffmpeg::tokenize_csv(k->second->extensions, exts); + list ext_list; + for (auto ext=exts.begin(); ext!=exts.end(); ++ext) ext_list.append(*ext); + property["extensions"] = tuple(ext_list); + + // get recommended codec + if (!k->second->video_codec) { + property["default_codec"] = object(); + } + else { + AVCodec* codec = avcodec_find_encoder(k->second->video_codec); + if (!codec) property["default_codec"] = object(); + else property["default_codec"] = describe_codec(codec); + } + + // supported codec list + std::vector<const AVCodec*> codecs; + bob::io::detail::ffmpeg::oformat_supported_codecs(k->second->name, codecs); + dict supported_codecs; + for (auto c=codecs.begin(); c!=codecs.end(); ++c) { + supported_codecs[(*c)->name] = describe_codec(*c); + } + property["supported_codecs"] = supported_codecs; + + retval[k->first] = property; + } + + return retval; +} +**/ + +/** +static dict available_oformat_dictionary() { + std::map<std::string, AVOutputFormat*> m; + bob::io::detail::ffmpeg::oformats_installed(m); + dict retval; + + for (auto k=m.begin(); k!=m.end(); ++k) { + dict property; + property["name"] = k->second->name; + property["long_name"] = k->second->long_name; + property["mime_type"] = k->second->mime_type; + + // get extensions + std::vector<std::string> exts; + bob::io::detail::ffmpeg::tokenize_csv(k->second->extensions, exts); + list ext_list; + for (auto ext=exts.begin(); ext!=exts.end(); ++ext) ext_list.append(*ext); + property["extensions"] = tuple(ext_list); + + // get recommended codec + if (!k->second->video_codec) { + property["default_codec"] = object(); + } + else { + AVCodec* codec = avcodec_find_encoder(k->second->video_codec); + if (!codec) property["default_codec"] = object(); + else property["default_codec"] = describe_codec(codec); + } + + retval[k->first] = property; + } + + return retval; +} +**/ + +/** + def("available_videoreader_formats", &available_iformat_dictionary, "Returns a dictionary containing a detailed description of the built-in input formats that are available, but **not necessarily supported**"); + + def("supported_videoreader_formats", &supported_iformat_dictionary, "Returns a dictionary containing a detailed description of the built-in input formats that are fully supported"); + + def("available_videowriter_formats", &available_oformat_dictionary, "Returns a dictionary containing a detailed description of the built-in output formats and default encoders for videos that are available, but **not necessarily supported**"); + + def("supported_videowriter_formats", &supported_oformat_dictionary, "Returns a dictionary containing a detailed description of the built-in output formats and default encoders for videos that are fully supported"); + +**/ + +#endif /* WITH_FFMPEG */ + +static PyObject* PyBobIo_Extensions(PyObject*) { + + typedef std::map<std::string, std::string> map_type; + const map_type& table = bob::io::CodecRegistry::getExtensions(); + + PyObject* retval = PyDict_New(); + if (!retval) return 0; + + for (auto it=table.begin(); it!=table.end(); ++it) { + PyObject* pyvalue = make_object(it->second.c_str()); + if (!pyvalue) { + Py_DECREF(retval); + return 0; + } + if (PyDict_SetItemString(retval, it->first.c_str(), pyvalue) != 0) { + Py_DECREF(pyvalue); + Py_DECREF(retval); + return 0; + } + Py_DECREF(pyvalue); + } + return retval; + +} + +PyDoc_STRVAR(s_extensions_str, "extensions"); +PyDoc_STRVAR(s_extensions_doc, +"as_blitz(x) -> dict\n\ +\n\ +Returns a dictionary containing all extensions and descriptions\n\ +currently stored on the global codec registry\n\ +"); + +static PyMethodDef module_methods[] = { + { + s_extensions_str, + (PyCFunction)PyBobIo_Extensions, + METH_NOARGS, + s_extensions_doc, + }, + +#if WITH_FFMPEG + { + s_describe_encoder_str, + (PyCFunction)PyBobIo_DescribeEncoder, + METH_VARARGS|METH_KEYWORDS, + s_describe_encoder_doc, + }, + { + s_describe_decoder_str, + (PyCFunction)PyBobIo_DescribeDecoder, + METH_VARARGS|METH_KEYWORDS, + s_describe_decoder_doc, + }, + { + s_supported_codecs_str, + (PyCFunction)PyBobIo_SupportedCodecs, + METH_NOARGS, + s_supported_codecs_doc, + }, + { + s_available_codecs_str, + (PyCFunction)PyBobIo_AvailableCodecs, + METH_NOARGS, + s_available_codecs_doc, + }, + +#endif /* WITH_FFMPEG */ + + {0} /* Sentinel */ +}; + +PyDoc_STRVAR(module_docstr, +"Information about software used to compile the C++ Bob API" +); + +int PyXbobCoreRandom_APIVersion = XBOB_IO_API_VERSION; + +#define ENTRY_FUNCTION_INNER(a) init ## a +#define ENTRY_FUNCTION(a) ENTRY_FUNCTION_INNER(a) + +PyMODINIT_FUNC ENTRY_FUNCTION(XBOB_IO_VERSIONS_MODULE_NAME) (void) { + + PyObject* m = Py_InitModule3(BOOST_PP_STRINGIZE(XBOB_IO_VERSIONS_MODULE_NAME), module_methods, module_docstr); + + /* register some constants */ + PyModule_AddIntConstant(m, "__api_version__", XBOB_IO_API_VERSION); + PyModule_AddStringConstant(m, "__version__", BOOST_PP_STRINGIZE(XBOB_IO_VERSION)); + PyModule_AddObject(m, "versions", build_version_dictionary()); + + /* imports the NumPy C-API */ + import_array(); + +} diff --git a/xbob/io/file.cpp b/xbob/io/file.cpp index 4187cc3632a3a99fda30881ab52e8e605933ec0a..70b5c20a823d4407de54fe51148ae1cc93071046 100644 --- a/xbob/io/file.cpp +++ b/xbob/io/file.cpp @@ -44,31 +44,30 @@ static int PyBobIoFile_Init(PyBobIoFileObject* self, PyObject *args, PyObject* k static char** kwlist = const_cast<char**>(const_kwlist); char* filename = 0; - char* mode = 0; - int mode_len = 0; + char mode = 0; char* pretend_extension = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss#|s", kwlist, &filename, - &mode, &mode_len, &pretend_extension)) return -1; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "sc|s", kwlist, &filename, + &mode, &pretend_extension)) return -1; - if (mode_len != 1 || !(mode[0] == 'r' || mode[0] == 'w' || mode[0] == 'a')) { + if (mode != 'r' && mode != 'w' && mode != 'a') { PyErr_Format(PyExc_ValueError, "file open mode string should have 1 element and be either 'r' (read), 'w' (write) or 'a' (append)"); return -1; } try { if (pretend_extension) { - self->f = bob::io::open(filename, mode[0], pretend_extension); + self->f = bob::io::open(filename, mode, pretend_extension); } else { - self->f = bob::io::open(filename, mode[0]); + self->f = bob::io::open(filename, mode); } } catch (std::exception& e) { - PyErr_Format(PyExc_RuntimeError, "cannot open file `%s' with mode `%s': %s", filename, mode, e.what()); + PyErr_Format(PyExc_RuntimeError, "cannot open file `%s' with mode `%c': %s", filename, mode, e.what()); return -1; } catch (...) { - PyErr_Format(PyExc_RuntimeError, "cannot open file `%s' with mode `%s': unknown exception caught", filename, mode); + PyErr_Format(PyExc_RuntimeError, "cannot open file `%s' with mode `%c': unknown exception caught", filename, mode); return -1; } @@ -76,7 +75,7 @@ static int PyBobIoFile_Init(PyBobIoFileObject* self, PyObject *args, PyObject* k } PyDoc_STRVAR(s_file_doc, -"file(filename, mode, [pretend_extension]) -> new bob::io::File\n\ +"File(filename, mode, [pretend_extension]) -> new bob::io::File\n\ \n\ Use this object to read and write data into files.\n\ \n\ @@ -455,6 +454,71 @@ Returns the current position of the newly written array.\n\ " ); +static PyObject* PyBobIoFile_Describe(PyBobIoFileObject* self, PyObject *args, PyObject* kwds) { + + /* Parses input arguments in a single shot */ + static const char* const_kwlist[] = {"all", 0}; + static char** kwlist = const_cast<char**>(const_kwlist); + + PyObject* all = 0; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &all)) return 0; + + if (all && !PyBool_Check(all)) { + PyErr_SetString(PyExc_TypeError, "argument to `all' must be a boolean"); + return 0; + } + + const bob::core::array::typeinfo* info = 0; + if (all && all == Py_True) info = &self->f->type_all(); + else info = &self->f->type(); + + /* Now return type description and tuples with shape and strides */ + int type_num = PyBobIo_AsTypenum(info->dtype); + if (type_num == NPY_NOTYPE) return 0; + + /* Get data-type */ + PyObject* dtype = reinterpret_cast<PyObject*>(PyArray_DescrFromType(type_num)); + + /* Build shape and stride */ + PyObject* shape = PyTuple_New(info->nd); + PyObject* stride = PyTuple_New(info->nd); + + for (Py_ssize_t i=0; i<info->nd; ++i) { +# if PY_VERSION_HEX >= 0x03000000 + PyObject* shape_item = PyLong_FromSsize_t(info->shape[i]); + PyObject* stride_item = PyLong_FromSsize_t(info->stride[i]); +# else + PyObject* shape_item = PyInt_FromSsize_t(info->shape[i]); + PyObject* stride_item = PyInt_FromSsize_t(info->stride[i]); +# endif + PyTuple_SetItem(shape, i, shape_item); + PyTuple_SetItem(stride, i, stride_item); + } + + /* Return a new tuple */ + PyObject* retval = PyTuple_New(3); + PyTuple_SetItem(retval, 0, dtype); + PyTuple_SetItem(retval, 1, shape); + PyTuple_SetItem(retval, 2, stride); + + return retval; + +} + +PyDoc_STRVAR(s_describe_str, "describe"); +PyDoc_STRVAR(s_describe_doc, +"describe([all]) -> tuple\n\ +\n\ +Returns a description (dtype, shape, stride) of data at the file.\n\ +\n\ +Parameters:\n\ +\n\ +all\n\ + [bool] If set, return the shape and strides for reading\n\ + the whole file contents in one go.\n\ +\n\ +"); + static PyMethodDef PyBobIoFile_Methods[] = { { s_read_str, @@ -474,6 +538,12 @@ static PyMethodDef PyBobIoFile_Methods[] = { METH_VARARGS|METH_KEYWORDS, s_append_doc, }, + { + s_describe_str, + (PyCFunction)PyBobIoFile_Describe, + METH_VARARGS|METH_KEYWORDS, + s_describe_doc, + }, {0} /* Sentinel */ }; @@ -518,26 +588,3 @@ PyTypeObject PyBobIoFile_Type = { 0, /* tp_alloc */ PyBobIoFile_New, /* tp_new */ }; - -/** -static dict extensions() { - typedef std::map<std::string, std::string> map_type; - dict retval; - const map_type& table = bob::io::CodecRegistry::getExtensions(); - for (map_type::const_iterator it=table.begin(); it!=table.end(); ++it) { - retval[it->first] = it->second; - } - return retval; -} - -void bind_io_file() { - - .add_property("type_all", make_function(&bob::io::File::type_all, return_value_policy<copy_const_reference>()), "Typing information to load all of the file at once") - - .add_property("type", make_function(&bob::io::File::type, return_value_policy<copy_const_reference>()), "Typing information to load the file as an Arrayset") - - def("extensions", &extensions, "Returns a dictionary containing all extensions and descriptions currently stored on the global codec registry"); - -} - -**/ diff --git a/xbob/io/include/xbob.io/api.h b/xbob/io/include/xbob.io/api.h index 64f54cfe1cbb4ef11bce8823917c37a2da7c15bc..f1a8e6afa1e6d3d4c5b18028dab54e116f1c8f65 100644 --- a/xbob/io/include/xbob.io/api.h +++ b/xbob/io/include/xbob.io/api.h @@ -10,12 +10,14 @@ #include <xbob.io/config.h> #include <bob/io/File.h> + +#if WITH_FFMPEG +#include <bob/io/VideoReader.h> +#endif /* WITH_FFMPEG */ + #include <boost/preprocessor/stringize.hpp> #include <boost/shared_ptr.hpp> - -extern "C" { #include <Python.h> -} #define XBOB_IO_MODULE_PREFIX xbob.io #define XBOB_IO_MODULE_NAME _library @@ -55,8 +57,31 @@ typedef struct { #define PyBobIo_AsTypenum_RET int #define PyBobIo_AsTypenum_PROTO (bob::core::array::ElementType et) +#if WITH_FFMPEG + +/****************** + * Video bindings * + ******************/ + +typedef struct { + PyObject_HEAD + + /* Type-specific fields go here. */ + boost::shared_ptr<bob::io::VideoReader> v; + +} PyBobIoVideoReaderObject; + +#define PyBobIoVideoReader_Type_NUM 3 +#define PyBobIoVideoReader_Type_TYPE PyTypeObject + +#endif /* WITH_FFMPEG */ + /* Total number of C API pointers */ -#define PyXbobIo_API_pointers 3 +#if WITH_FFMPEG +# define PyXbobIo_API_pointers 4 +#else +# define PyXbobIo_API_pointers 3 +#endif /* WITH_FFMPEG */ #ifdef XBOB_IO_MODULE @@ -80,6 +105,14 @@ typedef struct { PyBobIo_AsTypenum_RET PyBobIo_AsTypenum PyBobIo_AsTypenum_PROTO; +#if WITH_FFMPEG + /****************** + * Video bindings * + ******************/ + + extern PyBobIoVideoReader_Type_TYPE PyBobIoVideoReader_Type; +#endif /* WITH_FFMPEG */ + #else /* This section is used in modules that use `blitz.array's' C-API */ @@ -126,6 +159,14 @@ typedef struct { # define PyBobIo_AsTypenum (*(PyBobIo_AsTypenum_RET (*)PyBobIo_AsTypenum_PROTO) PyXbobIo_API[PyBobIo_AsTypenum_NUM]) +#if WITH_FFMPEG + /****************** + * Video bindings * + ******************/ + +# define PyBobIoVideoReader_Type (*(PyBobIoVideoReader_Type_TYPE *)PyXbobIo_API[PyBobIoVideoReader_Type_NUM]) +#endif /* WITH_FFMPEG */ + /** * Returns -1 on error, 0 on success. PyCapsule_Import will set an exception * if there's an error. diff --git a/xbob/io/main.cpp b/xbob/io/main.cpp index b5cccefff89679b9c1a9cb7c218e8c25bb68453c..58b855705fe38d0b484290a89a34983f539f3bb6 100644 --- a/xbob/io/main.cpp +++ b/xbob/io/main.cpp @@ -7,53 +7,13 @@ #define XBOB_IO_MODULE #include <xbob.io/api.h> -#include <bob/io/CodecRegistry.h> #ifdef NO_IMPORT_ARRAY #undef NO_IMPORT_ARRAY #endif #include <blitz.array/capi.h> -static PyObject* PyBobIo_Extensions(PyObject*) { - - typedef std::map<std::string, std::string> map_type; - const map_type& table = bob::io::CodecRegistry::getExtensions(); - - PyObject* retval = PyDict_New(); - if (!retval) return 0; - - for (auto it=table.begin(); it!=table.end(); ++it) { -# if PY_VERSION_HEX >= 0x03000000 - PyObject* value = PyString_FromString(it->second.c_str()); -# else - PyObject* value = PyUnicode_FromString(it->second.c_str()); -# endif - if (!value) { - Py_DECREF(retval); - return 0; - } - PyDict_SetItemString(retval, it->first.c_str(), value); - Py_DECREF(value); - } - return retval; - -} - -PyDoc_STRVAR(s_extensions_str, "extensions"); -PyDoc_STRVAR(s_extensions_doc, -"as_blitz(x) -> dict\n\ -\n\ -Returns a dictionary containing all extensions and descriptions\n\ -currently stored on the global codec registry\n\ -"); - static PyMethodDef module_methods[] = { - { - s_extensions_str, - (PyCFunction)PyBobIo_Extensions, - METH_NOARGS, - s_extensions_doc, - }, {0} /* Sentinel */ }; @@ -69,6 +29,11 @@ PyMODINIT_FUNC ENTRY_FUNCTION(XBOB_IO_MODULE_NAME) (void) { PyBobIoFile_Type.tp_new = PyType_GenericNew; if (PyType_Ready(&PyBobIoFile_Type) < 0) return; +#if WITH_FFMPEG + PyBobIoVideoReader_Type.tp_new = PyType_GenericNew; + if (PyType_Ready(&PyBobIoVideoReader_Type) < 0) return; +#endif /* WITH_FFMPEG */ + PyObject* m = Py_InitModule3(BOOST_PP_STRINGIZE(XBOB_IO_MODULE_NAME), module_methods, module_docstr); @@ -80,6 +45,11 @@ PyMODINIT_FUNC ENTRY_FUNCTION(XBOB_IO_MODULE_NAME) (void) { Py_INCREF(&PyBobIoFile_Type); PyModule_AddObject(m, "File", (PyObject *)&PyBobIoFile_Type); +#if WITH_FFMPEG + Py_INCREF(&PyBobIoVideoReader_Type); + PyModule_AddObject(m, "VideoReader", (PyObject *)&PyBobIoVideoReader_Type); +#endif /* WITH_FFMPEG */ + static void* PyXbobIo_API[PyXbobIo_API_pointers]; /* exhaustive list of C APIs */ @@ -102,6 +72,14 @@ PyMODINIT_FUNC ENTRY_FUNCTION(XBOB_IO_MODULE_NAME) (void) { PyXbobIo_API[PyBobIo_AsTypenum_NUM] = (void *)PyBobIo_AsTypenum; +#if WITH_FFMPEG + /****************** + * Video bindings * + ******************/ + + PyXbobIo_API[PyBobIoVideoReader_Type_NUM] = (void *)&PyBobIoVideoReader_Type; +#endif /* WITH_FFMPEG */ + /* imports the NumPy C-API */ import_array(); diff --git a/xbob/io/test/test_file.py b/xbob/io/test/test_file.py index 643bc7c02f35c2d5cd1bbcb03c8fb0bd8cc7afb5..0fa83f9f4221f6dd80436560c9f786aae527f3f9 100644 --- a/xbob/io/test/test_file.py +++ b/xbob/io/test/test_file.py @@ -14,7 +14,7 @@ import sys import numpy import nose.tools -from .. import load, write, File +from .. import load, write, peek, peek_all, File from . import utils as testutils def transcode(filename): @@ -272,3 +272,9 @@ def test_csv(): arrayset_readwrite('.csv', a1, close=True) arrayset_readwrite(".csv", a2, close=True) arrayset_readwrite('.csv', a3, close=True) + +def test_peek(): + + f = testutils.datafile('test1.hdf5', __name__) + assert peek(f) == (numpy.uint16, (3,), (1,)) + assert peek_all(f) == (numpy.uint16, (3,3), (3,1)) diff --git a/xbob/io/test/test_video.py b/xbob/io/test/test_video.py index 65991b00f6ef5cdbcaa9d07563dc3873485b150a..5627997dc16bf35ee50e80f0fc096b04a3d1a861 100644 --- a/xbob/io/test/test_video.py +++ b/xbob/io/test/test_video.py @@ -4,18 +4,6 @@ # Wed Jun 22 17:50:08 2011 +0200 # # Copyright (C) 2011-2013 Idiap Research Institute, Martigny, Switzerland -# -# 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, version 3 of the License. -# -# 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 <http://www.gnu.org/licenses/>. """Runs some video tests """ @@ -23,13 +11,31 @@ import os import sys import numpy +import nose.tools from . import utils as testutils -from .._library import supported_videowriter_formats from ..utils import color_distortion, frameskip_detection, quality_degradation # These are some global parameters for the test. INPUT_VIDEO = testutils.datafile('test.mov', __name__) -SUPPORTED = supported_videowriter_formats() + +@testutils.ffmpeg_found() +def test_codec_support(): + + # Describes all encoders + from .._externals import describe_encoder, describe_decoder, supported_video_codecs + + supported = supported_video_codecs() + + for k,v in supported.items(): + # note: searching by name (using `k') will not always work + if v['decode']: assert describe_decoder(v['id']) + if v['encode']: assert describe_encoder(v['id']) + + # Assert we support, at least, some known codecs + for codec in ('ffv1', 'zlib', 'wmv2', 'mpeg4', 'mjpeg'): + assert codec in supported + assert supported[codec]['encode'] + assert supported[codec]['decode'] @testutils.ffmpeg_found() def test_can_use_array_interface(): @@ -144,7 +150,8 @@ def test_format_codecs(): distortions['mpeg2video']['color'] = 9.0 distortions['mpeg2video']['frameskip'] = 1.4 - + from .._library import supported_videowriter_formats + SUPPORTED = supported_videowriter_formats() for format in SUPPORTED: for codec in SUPPORTED[format]['supported_codecs']: for method in methods: @@ -224,6 +231,8 @@ def test_user_video(): msmpeg4v2 = 2.3, ) + from .._library import supported_videowriter_formats + SUPPORTED = supported_videowriter_formats() for format in SUPPORTED: for codec in SUPPORTED[format]['supported_codecs']: check_user_video.description = "%s.test_user_video_format_%s_codec_%s" % (__name__, format, codec) diff --git a/xbob/io/test/utils.py b/xbob/io/test/utils.py index 34f103ced60ae27a56c8bba549f70d9159f861bf..5f72f91b8924fd7e8cb0fe7527619cddb3e401ef 100644 --- a/xbob/io/test/utils.py +++ b/xbob/io/test/utils.py @@ -2,6 +2,8 @@ # vim: set fileencoding=utf-8 : # Andre Anjos <andre.anjos@idiap.ch> # Thu Feb 7 09:58:22 2013 +# +# Copyright (C) 2011-2013 Idiap Research Institute, Martigny, Switzerland """Re-usable decorators and utilities for xbob test code """ @@ -59,14 +61,17 @@ ffmpeg_versions = { '0.11': [ SV('54.23.100'), SV('54.6.100'), SV('51.54.100') ], '1.0': [ SV('54.59.100'), SV('54.29.104'), SV('51.73.101') ], '1.1': [ SV('54.86.100'), SV('54.59.106'), SV('52.13.100') ], + '1.2': [ SV('54.92.100'), SV('54.63.104'), SV('52.18.100') ], + '2.0': [ SV('55.18.102'), SV('55.12.100'), SV('52.38.100') ], + '2.1': [ SV('55.39.100'), SV('55.19.104'), SV('52.48.100') ], } def ffmpeg_version_lessthan(v): '''Returns true if the version of ffmpeg compiled-in is at least the version indicated as a string parameter.''' - from ..io._io import version - avcodec_inst= SV(version['FFmpeg']['avcodec']) + from .._externals import versions + avcodec_inst= SV(versions['FFmpeg']['avcodec']) avcodec_req = ffmpeg_versions[v][0] return avcodec_inst < avcodec_req @@ -95,10 +100,10 @@ def ffmpeg_found(version_geq=None): @functools.wraps(test) def wrapper(*args, **kwargs): try: - from ..io._io import version - avcodec_inst= SV(version['FFmpeg']['avcodec']) - avformat_inst= SV(version['FFmpeg']['avformat']) - avutil_inst= SV(version['FFmpeg']['avutil']) + from .._externals import versions + avcodec_inst = SV(versions['FFmpeg']['avcodec']) + avformat_inst = SV(versions['FFmpeg']['avformat']) + avutil_inst = SV(versions['FFmpeg']['avutil']) if version_geq is not None: avcodec_req,avformat_req,avutil_req = ffmpeg_versions[version_geq] if avcodec_inst < avcodec_req: @@ -136,7 +141,7 @@ def extension_available(extension): @functools.wraps(test) def wrapper(*args, **kwargs): - from .._library import extensions + from .._externals import extensions if extension in extensions(): return test(*args, **kwargs) else: diff --git a/xbob/io/utils.py b/xbob/io/utils.py index a60aec7b9e513be8553ca027a2fa842dc0332cdf..9e3d67b7f83120050dab2bf357cc72489bd4b5c7 100644 --- a/xbob/io/utils.py +++ b/xbob/io/utils.py @@ -2,6 +2,8 @@ # vim: set fileencoding=utf-8 : # Andre Anjos <andre.dos.anjos@gmail.com> # Thu 14 Mar 17:00:58 2013 +# +# Copyright (C) 2011-2013 Idiap Research Institute, Martigny, Switzerland """Some utilities to generate fake patterns """ diff --git a/xbob/io/videoreader.cpp b/xbob/io/videoreader.cpp new file mode 100644 index 0000000000000000000000000000000000000000..51ab96d324f51dcd7c708eab05cfd73e05cdf6aa --- /dev/null +++ b/xbob/io/videoreader.cpp @@ -0,0 +1,185 @@ +/** + * @author Andre Anjos <andre.anjos@idiap.ch> + * @date Wed 6 Nov 21:44:34 2013 + * + * @brief Bindings to bob::io::VideoReader + */ + +#define XBOB_IO_MODULE +#include <xbob.io/api.h> + +#if WITH_FFMPEG +#include <boost/make_shared.hpp> + +#define VIDEOREADER_NAME VideoReader +PyDoc_STRVAR(s_videoreader_str, BOOST_PP_STRINGIZE(XBOB_IO_MODULE_PREFIX) "." BOOST_PP_STRINGIZE(VIDEOREADER_NAME)); + +/* How to create a new PyBobIoVideoReaderObject */ +static PyObject* PyBobIoVideoReader_New(PyTypeObject* type, PyObject*, PyObject*) { + + /* Allocates the python object itself */ + PyBobIoVideoReaderObject* self = (PyBobIoVideoReaderObject*)type->tp_alloc(type, 0); + + self->v.reset(); + + return reinterpret_cast<PyObject*>(self); +} + +static void PyBobIoVideoReader_Delete (PyBobIoVideoReaderObject* o) { + + o->v.reset(); + o->ob_type->tp_free((PyObject*)o); + +} + +/* The __init__(self) method */ +static int PyBobIoVideoReader_Init(PyBobIoVideoReaderObject* self, + PyObject *args, PyObject* kwds) { + + /* Parses input arguments in a single shot */ + static const char* const_kwlist[] = {"filename", "check", 0}; + static char** kwlist = const_cast<char**>(const_kwlist); + + char* filename = 0; + PyObject* pycheck = 0; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|O", kwlist, + &filename, &pycheck)) return -1; + + if (pycheck && !PyBool_Check(pycheck)) { + PyErr_SetString(PyExc_TypeError, "argument to `check' must be a boolean"); + return -1; + } + + bool check = false; + if (pycheck && (pycheck == Py_True)) check = true; + + try { + self->v = boost::make_shared<bob::io::VideoReader>(filename, check); + } + catch (std::exception& e) { + PyErr_Format(PyExc_RuntimeError, "cannot open video file `%s' for reading: %s", filename, e.what()); + return -1; + } + catch (...) { + PyErr_Format(PyExc_RuntimeError, "cannot open video file `%s' for reading: unknown exception caught", filename); + return -1; + } + + return 0; ///< SUCCESS +} + +PyDoc_STRVAR(s_videoreader_doc, +"VideoReader(filename, [check=True]) -> new bob::io::VideoReader\n\ +\n\ +Use this object to read frames from video files.\n\ +\n\ +Constructor parameters:\n\ +\n\ +filename\n\ + [str] The file path to the file you want to read data from\n\ +\n\ +check\n\ + [bool] Format and codec will be extracted from the video metadata.\n\ + By default, if the format and/or the codec are not\n\ + supported by this version of Bob, an exception will be raised.\n\ + You can (at your own risk) set this flag to ``False`` to\n\ + avoid this check.\n\ +\n\ +VideoReader objects can read data from video files. The current\n\ +implementation uses `FFmpeg <http://ffmpeg.org>`_ (or\n\ +`libav <http://libav.org>`_ if FFmpeg is not available) which is\n\ +a stable freely available video encoding and decoding library,\n\ +designed specifically for these tasks. You can read an entire\n\ +video in memory by using the 'load()' method or use iterators\n\ +to read it frame by frame and avoid overloading your machine's\n\ +memory. The maximum precision data `FFmpeg` will yield is a 24-bit\n\ +(8-bit per band) representation of each pixel (32-bit depths are\n\ +also supported by `FFmpeg`, but not by this extension presently).\n\ +So, the output of data is done with ``uint8`` as data type.\n\ +Output will be colored using the RGB standard, with each band\n\ +varying between 0 and 255, with zero meaning pure black and 255,\n\ +pure white (color).\n\ +"); + +PyTypeObject PyBobIoVideoReader_Type = { + PyObject_HEAD_INIT(0) + 0, /*ob_size*/ + s_videoreader_str, /*tp_name*/ + sizeof(PyBobIoVideoReaderObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyBobIoVideoReader_Delete, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, //(reprfunc)PyBobIoVideoReader_Repr, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, //&PyBobIoVideoReader_Sequence, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, //(reprfunc)PyBobIoVideoReader_Repr, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + s_videoreader_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, //PyBobIoVideoReader_Methods, /* tp_methods */ + 0, /* tp_members */ + 0, //PyBobIoVideoReader_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)PyBobIoVideoReader_Init, /* tp_init */ + 0, /* tp_alloc */ + PyBobIoVideoReader_New, /* tp_new */ +}; +/** + + .add_property("filename", make_function(&bob::io::VideoReader::filename, return_value_policy<copy_const_reference>()), "The full path to the file that will be decoded by this object") + + .add_property("height", &bob::io::VideoReader::height, "The height of each frame in the video (a multiple of 2)") + + .add_property("width", &bob::io::VideoReader::width, "The width of each frame in the video (a multiple of 2)") + + .add_property("number_of_frames", &bob::io::VideoReader::numberOfFrames, "The number of frames in this video file") + + .def("__len__", &bob::io::VideoReader::numberOfFrames) + + .add_property("duration", &bob::io::VideoReader::duration, "Total duration of this video file in microseconds (long)") + + .add_property("format_name", make_function(&bob::io::VideoReader::formatName, return_value_policy<copy_const_reference>()), "Short name of the format in which this video file was recorded in") + + .add_property("format_long_name", make_function(&bob::io::VideoReader::formatLongName, return_value_policy<copy_const_reference>()), "Verbose name of the format in which this video file was recorded in") + + .add_property("codec_name", make_function(&bob::io::VideoReader::codecName, return_value_policy<copy_const_reference>()), "Short name of the codec that will be used to decode this video file") + + .add_property("codec_long_name", make_function(&bob::io::VideoReader::codecLongName, return_value_policy<copy_const_reference>()), "Verbose name of the codec that will be used to decode this video file") + + .add_property("frame_rate", &bob::io::VideoReader::frameRate, "Video's announced frame rate (note there are video formats with variable frame rates)") + + .add_property("info", make_function(&bob::io::VideoReader::info, return_value_policy<copy_const_reference>()), "Informative string containing many details of this video and available ffmpeg bindings that will read it") + + .add_property("video_type", make_function(&bob::io::VideoReader::video_type, return_value_policy<copy_const_reference>()), "Typing information to load all of the file at once") + + .add_property("frame_type", make_function(&bob::io::VideoReader::frame_type, return_value_policy<copy_const_reference>()), "Typing information to load the file frame by frame.") + + .def("__load__", &videoreader_load, videoreader_load_overloads((arg("self"), arg("raise_on_error")=false), "Loads all of the video stream in a numpy ndarray organized in this way: (frames, color-bands, height, width). I'll dynamically allocate the output array and return it to you. The flag ``raise_on_error``, which is set to ``False`` by default influences the error reporting in case problems are found with the video file. If you set it to ``True``, we will report problems raising exceptions. If you either don't set it or set it to ``False``, we will truncate the file at the frame with problems and will not report anything. It is your task to verify if the number of frames returned matches the expected number of frames as reported by the property ``number_of_frames`` in this object.")) + + .def("__iter__", &bob::io::VideoReader::begin, with_custodian_and_ward_postcall<0,1>()) + + .def("__getitem__", &videoreader_getitem) + + .def("__getitem__", &videoreader_getslice) + +**/ + +#endif /* WITH_FFMPEG */