Skip to content
Snippets Groups Projects
version.cpp 23.9 KiB
Newer Older
/**
 * @author Andre Anjos <andre.anjos@idiap.ch>
 * @date Thu  7 Nov 13:50:16 2013
 *
 * @brief Binds configuration information available from bob
 */

André Anjos's avatar
André Anjos committed
#include <Python.h>

André Anjos's avatar
André Anjos committed
#ifdef NO_IMPORT_ARRAY
#undef NO_IMPORT_ARRAY
#endif
#define XBOB_IO_MODULE
#include <xbob.io/config.h>

#include <string>
#include <cstdlib>
#include <boost/preprocessor/stringize.hpp>
#include <boost/format.hpp>

André Anjos's avatar
André Anjos committed
#include <bob/config.h>
#include <bob/io/CodecRegistry.h>
#include <bob/io/VideoUtilities.h>

André Anjos's avatar
André Anjos committed
#include <xbob.blitz/capi.h>
#include <xbob.blitz/cleanup.h>

extern "C" {

#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.
André Anjos's avatar
André Anjos committed
// 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>
#  if LIBAVUTIL_VERSION_INT >= 0x320f01 //50.15.1 @ ffmpeg-0.6
#    include <libavutil/opt.h>
#    include <libavutil/pixdesc.h>
#  endif
#  include <libavutil/avutil.h>
#  include <libswscale/swscale.h>
André Anjos's avatar
André Anjos committed
#  if !HAVE_FFMPEG_AVCODEC_AVCODECID
#    define AVCodecID CodecID
#  endif
#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;
André Anjos's avatar
André Anjos committed
  auto v_ = make_safe(v);
  int retval = PyDict_SetItemString(d, key, 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;
André Anjos's avatar
André Anjos committed
  auto value_ = make_safe(value);
  int retval = PyDict_SetItemString(d, key, 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(PyObject* l, T value) {
  PyObject* pyvalue = make_object(value);
  if (!pyvalue) return 0;
  int retval = PyList_Append(l, 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;
André Anjos's avatar
André Anjos committed
  auto retval_ = make_safe(retval);

#if WITH_FFMPEG
# if defined(FFMPEG_VERSION)
  if (std::strlen(FFMPEG_VERSION)) {
André Anjos's avatar
André Anjos committed
    if (!dict_set(retval, "ffmpeg", FFMPEG_VERSION)) return 0;
  }
# endif
  if (!dict_set(retval, "avformat", BOOST_PP_STRINGIZE(LIBAVFORMAT_VERSION))) {
    return 0;
  }
  if (!dict_set(retval, "avcodec", BOOST_PP_STRINGIZE(LIBAVCODEC_VERSION))) {
    return 0;
  }
  if (!dict_set(retval, "avutil", BOOST_PP_STRINGIZE(LIBAVUTIL_VERSION))) {
    return 0;
  }
  if (!dict_set(retval, "swscale", BOOST_PP_STRINGIZE(LIBSWSCALE_VERSION))) {
    return 0;
  }
#else
  if (!dict_set(retval, "ffmpeg", "unavailable")) {
    return 0;
  }
#endif
André Anjos's avatar
André Anjos committed
  Py_INCREF(retval);
  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)
André Anjos's avatar
André Anjos committed
    vtiff = vtiff.substr(beg_len);

  // Remove multiple (copyright) lines if any
  size_t end_line = vtiff.find("\n");
  if(end_line != std::string::npos)
André Anjos's avatar
André Anjos committed
    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;
André Anjos's avatar
André Anjos committed
  auto retval_ = make_safe(retval);
André Anjos's avatar
André Anjos committed
  if (!dict_steal(retval, "HDF5", hdf5_version())) return 0;
André Anjos's avatar
André Anjos committed
  if (!dict_steal(retval, "FFmpeg", ffmpeg_version())) return 0;
André Anjos's avatar
André Anjos committed
  if (!dict_steal(retval, "libjpeg", libjpeg_version())) return 0;
André Anjos's avatar
André Anjos committed
  if (!dict_set(retval, "libnetpbm", "Unknown version")) return 0;
André Anjos's avatar
André Anjos committed
  if (!dict_steal(retval, "libpng", libpng_version())) return 0;
André Anjos's avatar
André Anjos committed
  if (!dict_steal(retval, "libtiff", libtiff_version())) return 0;
André Anjos's avatar
André Anjos committed
  if (!dict_steal(retval, "giflib", giflib_version())) return 0;
André Anjos's avatar
André Anjos committed
  if (!dict_steal(retval, "MatIO", matio_version())) return 0;
André Anjos's avatar
André Anjos committed
  Py_INCREF(retval);
  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) {
André Anjos's avatar
André Anjos committed
      if (!list_append(pixfmt.get(),
#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.get(), ((double)rate->num)/((double)rate->den));
    ++rate;
  }
  rates.reset(PySequence_Tuple(rates.get()), &pyobject_deleter);
  if (!rates) return 0;
André Anjos's avatar
André Anjos committed

  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)) {
André Anjos's avatar
André Anjos committed

    /* 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)) {
André Anjos's avatar
André Anjos committed

    /* 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* get_video_codecs(void (*f)(std::map<std::string, const AVCodec*>&)) {
André Anjos's avatar
André Anjos committed

  std::map<std::string, const AVCodec*> m;
André Anjos's avatar
André Anjos committed

  PyObject* retval = PyDict_New();
  if (!retval) return 0;
André Anjos's avatar
André Anjos committed
  auto retval_ = make_safe(retval);

  for (auto k=m.begin(); k!=m.end(); ++k) {
    PyObject* pyvalue = describe_codec(k->second);
André Anjos's avatar
André Anjos committed
    if (!pyvalue) return 0;
    auto pyvalue_ = make_safe(pyvalue);
    if (PyDict_SetItemString(retval, k->first.c_str(), pyvalue) != 0) return 0;
André Anjos's avatar
André Anjos committed
  Py_INCREF(retval);
static PyObject* PyBobIo_SupportedCodecs(PyObject*) {
  return get_video_codecs(&bob::io::detail::ffmpeg::codecs_supported);
}

static PyObject* PyBobIo_AvailableCodecs(PyObject*) {
  return get_video_codecs(&bob::io::detail::ffmpeg::codecs_installed);
}

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\
");

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\
");

static PyObject* get_video_iformats(void (*f)(std::map<std::string, AVInputFormat*>&)) {

  std::map<std::string, AVInputFormat*> m;
André Anjos's avatar
André Anjos committed

  PyObject* retval = PyDict_New();
  if (!retval) return 0;
André Anjos's avatar
André Anjos committed
  auto retval_ = make_safe(retval);

  for (auto k=m.begin(); k!=m.end(); ++k) {

    PyObject* property = PyDict_New();
André Anjos's avatar
André Anjos committed
    if (!property) return 0;
    auto property_ = make_safe(property);
André Anjos's avatar
André Anjos committed
    if (!dict_set(property, "name", k->second->name)) return 0;
André Anjos's avatar
André Anjos committed

André Anjos's avatar
André Anjos committed
    if (!dict_set(property, "long_name", k->second->long_name)) return 0;

    // get extensions
    std::vector<std::string> exts;
    bob::io::detail::ffmpeg::tokenize_csv(k->second->extensions, exts);
André Anjos's avatar
André Anjos committed

    PyObject* ext_list = PyList_New(0);
André Anjos's avatar
André Anjos committed
    if (!ext_list) return 0;
    auto ext_list_ = make_safe(ext_list);

    for (auto ext=exts.begin(); ext!=exts.end(); ++ext) {
André Anjos's avatar
André Anjos committed
      if (!list_append(ext_list, ext->c_str())) return 0;
André Anjos's avatar
André Anjos committed
    Py_INCREF(ext_list);
    if (!dict_steal(property, "extensions", ext_list)) return 0;
André Anjos's avatar
André Anjos committed
    Py_INCREF(property);
    if (!dict_steal(retval, k->first.c_str(), property)) return 0;
André Anjos's avatar
André Anjos committed
  Py_INCREF(retval);
static PyObject* PyBobIo_SupportedInputFormats(PyObject*) {
André Anjos's avatar
André Anjos committed
  return get_video_iformats(&bob::io::detail::ffmpeg::iformats_supported);
static PyObject* PyBobIo_AvailableInputFormats(PyObject*) {
André Anjos's avatar
André Anjos committed
  return get_video_iformats(&bob::io::detail::ffmpeg::iformats_installed);
PyDoc_STRVAR(s_supported_iformats_str, "supported_videoreader_formats");
PyDoc_STRVAR(s_supported_iformats_doc,
"supported_videoreader_formats() -> dict\n\
\n\
Returns a dictionary with currently supported video input formats.\n\
\n\
Returns a dictionary containing a detailed description of the\n\
built-in input formats for videos that are fully supported.\n\
");
PyDoc_STRVAR(s_available_iformats_str, "available_videoreader_formats");
PyDoc_STRVAR(s_available_iformats_doc,
"available_videoreader_formats() -> dict\n\
\n\
Returns a dictionary with currently available video input formats.\n\
\n\
Returns a dictionary containing a detailed description of the\n\
built-in input formats for videos that are available, but **not\n\
necessarily supported** by this library.\n\
");
static PyObject* get_video_oformats(bool supported) {

  std::map<std::string, AVOutputFormat*> m;
  if (supported) bob::io::detail::ffmpeg::oformats_supported(m);
  else bob::io::detail::ffmpeg::oformats_installed(m);
André Anjos's avatar
André Anjos committed

  PyObject* retval = PyDict_New();
  if (!retval) return 0;
André Anjos's avatar
André Anjos committed
  auto retval_ = make_safe(retval);

  for (auto k=m.begin(); k!=m.end(); ++k) {

    PyObject* property = PyDict_New();
André Anjos's avatar
André Anjos committed
    if (!property) return 0;
    auto property_ = make_safe(property);
André Anjos's avatar
André Anjos committed
    if (!dict_set(property, "name", k->second->name)) return 0;
André Anjos's avatar
André Anjos committed

André Anjos's avatar
André Anjos committed
    if (!dict_set(property, "long_name", k->second->long_name)) return 0;
André Anjos's avatar
André Anjos committed
    if (!dict_set(property, "mime_type", k->second->mime_type)) return 0;

    // get extensions
    std::vector<std::string> exts;
    bob::io::detail::ffmpeg::tokenize_csv(k->second->extensions, exts);
André Anjos's avatar
André Anjos committed

    PyObject* ext_list = PyList_New(0);
André Anjos's avatar
André Anjos committed
    if (!ext_list) return 0;
    auto ext_list_ = make_safe(ext_list);
    for (auto ext=exts.begin(); ext!=exts.end(); ++ext) {
André Anjos's avatar
André Anjos committed
      if (!list_append(ext_list, ext->c_str())) return 0;
André Anjos's avatar
André Anjos committed
    Py_INCREF(ext_list);
    if (!dict_steal(property, "extensions", ext_list)) return 0;

    /**  get recommended codec **/
    PyObject* default_codec = 0;
    if (k->second->video_codec) {
      AVCodec* codec = avcodec_find_encoder(k->second->video_codec);
      if (codec) {
        default_codec = describe_codec(codec);
André Anjos's avatar
André Anjos committed
        if (!default_codec) return 0;
      }
    }

    if (!default_codec) {
      Py_INCREF(Py_None);
      default_codec = Py_None;
André Anjos's avatar
André Anjos committed
    if (!dict_steal(property, "default_codec", default_codec)) return 0;

    /** get supported codec list **/
    if (supported) {
      std::vector<const AVCodec*> codecs;
      bob::io::detail::ffmpeg::oformat_supported_codecs(k->second->name, codecs);

      PyObject* supported_codecs = PyDict_New();
André Anjos's avatar
André Anjos committed
      if (!supported_codecs) return 0;
      auto supported_codecs_ = make_safe(supported_codecs);

      for (auto c=codecs.begin(); c!=codecs.end(); ++c) {
        PyObject* codec_descr = describe_codec(*c);
André Anjos's avatar
André Anjos committed
        auto codec_descr_ = make_safe(codec_descr);
        if (!codec_descr) return 0;
        Py_INCREF(codec_descr);
        if (!dict_steal(supported_codecs, (*c)->name, codec_descr)) return 0;
André Anjos's avatar
André Anjos committed
      Py_INCREF(supported_codecs);
      if (!dict_steal(property, "supported_codecs", supported_codecs)) return 0;
André Anjos's avatar
André Anjos committed
    Py_INCREF(property);
    if (!dict_steal(retval, k->first.c_str(), property)) return 0;
André Anjos's avatar
André Anjos committed
  Py_INCREF(retval);
André Anjos's avatar
André Anjos committed

static PyObject* PyBobIo_SupportedOutputFormats(PyObject*) {
  return get_video_oformats(true);
static PyObject* PyBobIo_AvailableOutputFormats(PyObject*) {
  return get_video_oformats(false);
PyDoc_STRVAR(s_supported_oformats_str, "supported_videowriter_formats");
PyDoc_STRVAR(s_supported_oformats_doc,
"supported_videowriter_formats() -> dict\n\
\n\
Returns a dictionary with currently supported video output formats.\n\
\n\
Returns a dictionary containing a detailed description of the\n\
built-in output formats for videos that are fully supported.\n\
");
PyDoc_STRVAR(s_available_oformats_str, "available_videowriter_formats");
PyDoc_STRVAR(s_available_oformats_doc,
"available_videowriter_formats() -> dict\n\
\n\
Returns a dictionary with currently available video output formats.\n\
\n\
Returns a dictionary containing a detailed description of the\n\
built-in output formats for videos that are available, but **not\n\
necessarily supported** by this library.\n\
");

#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;
André Anjos's avatar
André Anjos committed
  auto retval_ = make_safe(retval);

  for (auto it=table.begin(); it!=table.end(); ++it) {
    PyObject* pyvalue = make_object(it->second.c_str());
André Anjos's avatar
André Anjos committed
    if (!pyvalue) return 0;
    if (PyDict_SetItemString(retval, it->first.c_str(), pyvalue) != 0) {
      return 0;
    }

  Py_INCREF(retval);
  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,
    },
    {
      s_supported_iformats_str,
      (PyCFunction)PyBobIo_SupportedInputFormats,
      METH_NOARGS,
      s_supported_iformats_doc,
    },
    {
      s_available_iformats_str,
      (PyCFunction)PyBobIo_AvailableInputFormats,
      METH_NOARGS,
      s_available_iformats_doc,
    },
    {
      s_supported_oformats_str,
      (PyCFunction)PyBobIo_SupportedOutputFormats,
      METH_NOARGS,
      s_supported_oformats_doc,
    },
    {
      s_available_oformats_str,
      (PyCFunction)PyBobIo_AvailableOutputFormats,
      METH_NOARGS,
      s_available_oformats_doc,
    },
#endif /* WITH_FFMPEG */

    {0}  /* Sentinel */
};

PyDoc_STRVAR(module_docstr,
"Information about software used to compile the C++ Bob API"
);

André Anjos's avatar
André Anjos committed
#if PY_VERSION_HEX >= 0x03000000
static PyModuleDef module_definition = {
  PyModuleDef_HEAD_INIT,
  XBOB_EXT_MODULE_NAME,
  module_docstr,
  -1,
  module_methods,
André Anjos's avatar
André Anjos committed
  0, 0, 0, 0
};
#endif

André Anjos's avatar
André Anjos committed
static PyObject* create_module (void) {
André Anjos's avatar
André Anjos committed
# if PY_VERSION_HEX >= 0x03000000
  PyObject* m = PyModule_Create(&module_definition);
# else
André Anjos's avatar
André Anjos committed
  PyObject* m = Py_InitModule3(XBOB_EXT_MODULE_NAME, module_methods, module_docstr);
André Anjos's avatar
André Anjos committed
# endif
André Anjos's avatar
André Anjos committed
  if (!m) return 0;
  auto m_ = make_safe(m); ///< protects against early returns
André Anjos's avatar
André Anjos committed
  /* register version numbers and constants */
  if (PyModule_AddIntConstant(m, "api", XBOB_IO_API_VERSION) < 0)
André Anjos's avatar
André Anjos committed
    return 0;
  if (PyModule_AddStringConstant(m, "module", XBOB_EXT_MODULE_VERSION) < 0)
André Anjos's avatar
André Anjos committed
    return 0;
  if (PyModule_AddObject(m, "externals", build_version_dictionary()) < 0) return 0;
André Anjos's avatar
André Anjos committed
  /* imports xbob.blitz C-API + dependencies */
  if (import_xbob_blitz() < 0) return 0;
André Anjos's avatar
André Anjos committed
  Py_INCREF(m);
André Anjos's avatar
André Anjos committed
  return m;

André Anjos's avatar
André Anjos committed

PyMODINIT_FUNC XBOB_EXT_ENTRY_NAME (void) {
# if PY_VERSION_HEX >= 0x03000000
  return
# endif
    create_module();
}