text: /**
   * @author Andre Anjos <andre.anjos@idiap.ch>
   * @date Wed  6 Nov 21:44:34 2013
   *
   * @brief Bindings to bob::io::video::Writer
   */
  
  #include "main.h"
  
  static auto s_writer = bob::extension::ClassDoc(
    "writer",
    "Use this object to write frames to video files"
    "Video writer objects can write data to video files. "
    "The current implementation uses `FFmpeg <http://ffmpeg.org>`_ (or `libav <http://libav.org>`_ if FFmpeg is not available) which is a stable freely available video encoding and decoding library, designed specifically for these tasks. "
    "Videos are objects composed of RGB colored frames. "
    "Each frame inserted should be a 3D :py:class:`numpy.ndarray` composed of unsigned integers of 8 bits. "
    "Each frame should have a shape equivalent to ``(plane, height, width)``."
  ).add_constructor(
    bob::extension::FunctionDoc(
      "writer",
      "Create a video writer",
      "The video will be created if the combination of format and codec are known to work and have been tested, otherwise an exception is raised. "
      "If you set the ``check`` parameter to ``False``, though, we will ignore this check.",
      true
    )
    .add_prototype("filename, height, width, [framerate], [bitrate], [gop], [codec], [format], [check]", "")
    .add_parameter("filename", "str", "The file path to the file you want to write data to")
    .add_parameter("height", "int", "The height of the video (must be a multiple of 2)")
    .add_parameter("width", "int", "The width of the video (must be a multiple of 2)")
    .add_parameter("framerate", "float", "[Default: 25.] The number of frames per second")
    .add_parameter("bitrate", "float", "[Default: 150000.] The estimated bitrate of the output video")
    .add_parameter("gop", "int", "[Default: 12] Group-of-Pictures (emit one intra frame every ``gop`` frames at most)")
    .add_parameter("codec", "str", "[Default: ``''``] If you must, specify a valid FFmpeg codec name here and that will be used to encode the video stream on the output file")
    .add_parameter("format", "str", "[Default: ``''``] If you must, specify a valid FFmpeg output format name and that will be used to encode the video on the output file. Leave it empty to guess from the filename extension")
    .add_parameter("check", "bool", "[Default: ``True``] ")
  );
  static auto s_fullname = BOB_EXT_MODULE_PREFIX ".writer";
  
  
  static void PyBobIoVideoWriter_Delete (PyBobIoVideoWriterObject* o) {
  
    o->v.reset();
    Py_TYPE(o)->tp_free((PyObject*)o);
  
  }
  
  /* The __init__(self) method */
  static int PyBobIoVideoWriter_Init(PyBobIoVideoWriterObject* self,
      PyObject *args, PyObject* kwds) {
  BOB_TRY
  
    /* Parses input arguments in a single shot */
    char** kwlist = s_writer.kwlist();
  
    char* filename = 0;
  
    Py_ssize_t height = 0;
    Py_ssize_t width = 0;
  
    double framerate = 25.;
    double bitrate = 1500000.;
    Py_ssize_t gop = 12;
    char* codec = 0;
    char* format = 0;
    PyObject* pycheck = Py_True;
  
    if (!PyArg_ParseTupleAndKeywords(args, kwds, "snn|ddnssO", kwlist,
          &filename,
          &height, &width, &framerate, &bitrate, &gop, &codec,
          &format, &pycheck)) return -1;
  
    std::string codec_str = codec?codec:"";
    std::string format_str = format?format:"";
    bool check = PyObject_IsTrue(pycheck);
  
    self->v = boost::make_shared<bob::io::video::Writer>(filename,
        height, width, framerate, bitrate, gop, codec_str, format_str, check);
  
    return 0; ///< SUCCESS
  BOB_CATCH_MEMBER("constructor", -1)
  }
  
  static auto s_filename = bob::extension::VariableDoc(
    "filename",
    "str",
    "The full path to the file that will be decoded by this object"
  );
  PyObject* PyBobIoVideoWriter_Filename(PyBobIoVideoWriterObject* self) {
    return Py_BuildValue("s", self->v->filename().c_str());
  }
  
  static auto s_height = bob::extension::VariableDoc(
    "height",
    "int",
    "The height of each frame in the video (a multiple of 2)"
  );
  PyObject* PyBobIoVideoWriter_Height(PyBobIoVideoWriterObject* self) {
    return Py_BuildValue("n", self->v->height());
  }
  
  static auto s_width = bob::extension::VariableDoc(
    "width",
    "int",
    "The width of each frame in the video (a multiple of 2)"
  );
  PyObject* PyBobIoVideoWriter_Width(PyBobIoVideoWriterObject* self) {
    return Py_BuildValue("n", self->v->width());
  }
  
  static auto s_number_of_frames = bob::extension::VariableDoc(
    "number_of_frames",
    "int",
    "The number of frames in this video file"
  );
  PyObject* PyBobIoVideoWriter_NumberOfFrames(PyBobIoVideoWriterObject* self) {
    return Py_BuildValue("n", self->v->numberOfFrames());
  }
  
  static auto s_duration = bob::extension::VariableDoc(
    "duration",
    "int",
    "Total duration of this video file in microseconds (long)"
  );
  PyObject* PyBobIoVideoWriter_Duration(PyBobIoVideoWriterObject* self) {
    return Py_BuildValue("n", self->v->duration());
  }
  
  static auto s_format_name = bob::extension::VariableDoc(
    "format_name",
    "str",
    "Short name of the format in which this video file will be written in"
  );
  PyObject* PyBobIoVideoWriter_FormatName(PyBobIoVideoWriterObject* self) {
    if (!self->v->is_opened()) {
      PyErr_Format(PyExc_RuntimeError, "`%s' for `%s' is closed",
          Py_TYPE(self)->tp_name, self->v->filename().c_str());
      return 0;
    }
    return Py_BuildValue("s", self->v->formatName().c_str());
  }
  
  static auto s_format_long_name = bob::extension::VariableDoc(
    "format_long_name",
    "str",
    "Full name of the format in which this video file will be written in"
  );
  PyObject* PyBobIoVideoWriter_FormatLongName(PyBobIoVideoWriterObject* self) {
    if (!self->v->is_opened()) {
      PyErr_Format(PyExc_RuntimeError, "`%s' for `%s' is closed",
          Py_TYPE(self)->tp_name, self->v->filename().c_str());
      return 0;
    }
    return Py_BuildValue("s", self->v->formatLongName().c_str());
  }
  
  static auto s_codec_name = bob::extension::VariableDoc(
    "codec_name",
    "str",
    "Short name of the codec in which this video file will be written in"
  );
  PyObject* PyBobIoVideoWriter_CodecName(PyBobIoVideoWriterObject* self) {
    if (!self->v->is_opened()) {
      PyErr_Format(PyExc_RuntimeError, "`%s' for `%s' is closed",
          Py_TYPE(self)->tp_name, self->v->filename().c_str());
      return 0;
    }
    return Py_BuildValue("s", self->v->codecName().c_str());
  }
  
  static auto s_codec_long_name = bob::extension::VariableDoc(
    "codec_long_name",
    "str",
    "Full name of the codec in which this video file will be written in"
  );
  PyObject* PyBobIoVideoWriter_CodecLongName(PyBobIoVideoWriterObject* self) {
    if (!self->v->is_opened()) {
      PyErr_Format(PyExc_RuntimeError, "`%s' for `%s' is closed",
          Py_TYPE(self)->tp_name, self->v->filename().c_str());
      return 0;
    }
    return Py_BuildValue("s", self->v->codecLongName().c_str());
  }
  
  static auto s_frame_rate = bob::extension::VariableDoc(
    "frame_rate",
    "float",
    "Video's announced frame rate (note there are video formats with variable frame rates)"
  );
  PyObject* PyBobIoVideoWriter_FrameRate(PyBobIoVideoWriterObject* self) {
    return PyFloat_FromDouble(self->v->frameRate());
  }
  
  static auto s_bit_rate = bob::extension::VariableDoc(
    "bit_rate",
    "float",
    "The indicative bit rate for this video file, given as a hint to `FFmpeg` (compression levels are subject to the picture textures)"
  );
  PyObject* PyBobIoVideoWriter_BitRate(PyBobIoVideoWriterObject* self) {
    return PyFloat_FromDouble(self->v->bitRate());
  }
  
  static auto s_gop = bob::extension::VariableDoc(
    "gop",
    "int",
    "Group of pictures setting (see the `Wikipedia entry <http://en.wikipedia.org/wiki/Group_of_pictures>`_ for details on this setting)"
  );
  PyObject* PyBobIoVideoWriter_GOP(PyBobIoVideoWriterObject* self) {
    return Py_BuildValue("n", self->v->gop());
  }
  
  static auto s_video_type = bob::extension::VariableDoc(
    "video_type",
    "tuple",
    "Typing information to load all of the file at once",
    ".. todo:: Explain, what exactly is contained in this tuple"
  );
  PyObject* PyBobIoVideoWriter_VideoType(PyBobIoVideoWriterObject* self) {
    return PyBobIo_TypeInfoAsTuple(self->v->video_type());
  }
  
  static auto s_frame_type = bob::extension::VariableDoc(
    "frame_type",
    "tuple",
    "Typing information to load each frame separatedly",
    ".. todo:: Explain, what exactly is contained in this tuple"
  );
  PyObject* PyBobIoVideoWriter_FrameType(PyBobIoVideoWriterObject* self) {
    return PyBobIo_TypeInfoAsTuple(self->v->frame_type());
  }
  
  static auto s_info = bob::extension::VariableDoc(
    "info",
    "str",
    "A string with lots of video information (same as ``str(x)``)"
  );
  static PyObject* PyBobIoVideoWriter_Print(PyBobIoVideoWriterObject* self) {
    if (!self->v->is_opened()) {
      PyErr_Format(PyExc_RuntimeError, "`%s' for `%s' is closed",
          Py_TYPE(self)->tp_name, self->v->filename().c_str());
      return 0;
    }
  
    return Py_BuildValue("s", self->v->info().c_str());
  }
  
  static auto s_is_opened = bob::extension::VariableDoc(
    "is_opened",
    "bool",
    "A flag, indicating if the video is still opened for writing (or has already been closed by the user using :py:meth:`close`)"
  );
  static PyObject* PyBobIoVideoWriter_IsOpened(PyBobIoVideoWriterObject* self) {
    if (self->v->is_opened()) Py_RETURN_TRUE;
    Py_RETURN_FALSE;
  }
  
  static PyGetSetDef PyBobIoVideoWriter_getseters[] = {
      {
        s_filename.name(),
        (getter)PyBobIoVideoWriter_Filename,
        0,
        s_filename.doc(),
        0,
      },
      {
        s_height.name(),
        (getter)PyBobIoVideoWriter_Height,
        0,
        s_height.doc(),
        0,
      },
      {
        s_width.name(),
        (getter)PyBobIoVideoWriter_Width,
        0,
        s_width.doc(),
        0,
      },
      {
        s_number_of_frames.name(),
        (getter)PyBobIoVideoWriter_NumberOfFrames,
        0,
        s_number_of_frames.doc(),
        0,
      },
      {
        s_duration.name(),
        (getter)PyBobIoVideoWriter_Duration,
        0,
        s_duration.doc(),
        0,
      },
      {
        s_format_name.name(),
        (getter)PyBobIoVideoWriter_FormatName,
        0,
        s_format_name.doc(),
        0,
      },
      {
        s_format_long_name.name(),
        (getter)PyBobIoVideoWriter_FormatLongName,
        0,
        s_format_long_name.doc(),
        0,
      },
      {
        s_codec_name.name(),
        (getter)PyBobIoVideoWriter_CodecName,
        0,
        s_codec_name.doc(),
        0,
      },
      {
        s_codec_long_name.name(),
        (getter)PyBobIoVideoWriter_CodecLongName,
        0,
        s_codec_long_name.doc(),
        0,
      },
      {
        s_frame_rate.name(),
        (getter)PyBobIoVideoWriter_FrameRate,
        0,
        s_frame_rate.doc(),
        0,
      },
      {
        s_bit_rate.name(),
        (getter)PyBobIoVideoWriter_BitRate,
        0,
        s_bit_rate.doc(),
        0,
      },
      {
        s_gop.name(),
        (getter)PyBobIoVideoWriter_GOP,
        0,
        s_gop.doc(),
        0,
      },
      {
        s_video_type.name(),
        (getter)PyBobIoVideoWriter_VideoType,
        0,
        s_video_type.doc(),
        0,
      },
      {
        s_frame_type.name(),
        (getter)PyBobIoVideoWriter_FrameType,
        0,
        s_frame_type.doc(),
        0,
      },
      {
        s_info.name(),
        (getter)PyBobIoVideoWriter_Print,
        0,
        s_info.doc(),
        0,
      },
      {
        s_is_opened.name(),
        (getter)PyBobIoVideoWriter_IsOpened,
        0,
        s_is_opened.doc(),
        0,
      },
      {0}  /* Sentinel */
  };
  
  static PyObject* PyBobIoVideoWriter_Repr(PyBobIoVideoWriterObject* self) {
    if (!self->v->is_opened()) {
      PyErr_Format(PyExc_RuntimeError, "`%s' for `%s' is closed",
          Py_TYPE(self)->tp_name, self->v->filename().c_str());
      return 0;
    }
  
    return
  # if PY_VERSION_HEX >= 0x03000000
    PyUnicode_FromFormat
  # else
    PyString_FromFormat
  # endif
    ("%s(filename='%s', height=%" PY_FORMAT_SIZE_T "d, width=%" PY_FORMAT_SIZE_T "d, framerate=%g, bitrate=%g, gop=%" PY_FORMAT_SIZE_T "d, codec='%s', format='%s')", Py_TYPE(self)->tp_name, self->v->filename().c_str(), self->v->height(), self->v->width(), self->v->frameRate(), self->v->bitRate(), self->v->gop(), self->v->codecName().c_str(), self->v->formatName().c_str());
  }
  
  
  static auto s_append = bob::extension::FunctionDoc(
    "append",
    "Writes a new frame or set of frames to the file.",
    "The frame should be setup as a array with 3 dimensions organized in this way (RGB color-bands, height, width). "
    "Sets of frames should be setup as a 4D array in this way: (frame-number, RGB color-bands, height, width). "
    "Arrays should contain only unsigned integers of 8 bits.\n\n"
    ".. note::\n"
    "  At present time we only support arrays that have C-style storages (if you pass reversed arrays or arrays with Fortran-style storage, the result is undefined).",
    true
  )
  .add_prototype("frame")
  .add_parameter("frame", "3D or 4D :py:class:`numpy.ndarray` of ``uint8``", "The frame or set of frames to write")
  ;
  static PyObject* PyBobIoVideoWriter_Append(PyBobIoVideoWriterObject* self, PyObject *args, PyObject* kwds) {
  BOB_TRY
    if (!self->v->is_opened()) {
      PyErr_Format(PyExc_RuntimeError, "`%s' for `%s' is closed",
          Py_TYPE(self)->tp_name, self->v->filename().c_str());
      return 0;
    }
  
    char** kwlist = s_append.kwlist();
  
    PyBlitzArrayObject* frame = 0;
    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&", kwlist, &PyBlitzArray_BehavedConverter, &frame)) return 0;
    auto frame_ = make_safe(frame);
  
    if (frame->ndim != 3 && frame->ndim != 4) {
      PyErr_Format(PyExc_ValueError, "input array should have 3 or 4 dimensions, but you passed an array with %" PY_FORMAT_SIZE_T "d dimensions", frame->ndim);
      return 0;
    }
  
    if (frame->type_num != NPY_UINT8) {
      PyErr_Format(PyExc_TypeError, "input array should have dtype `uint8', but you passed an array with dtype == `%s'", PyBlitzArray_TypenumAsString(frame->type_num));
      return 0;
    }
  
    if (frame->ndim == 3) {
      self->v->append(*PyBlitzArrayCxx_AsBlitz<uint8_t,3>(frame));
    }
    else {
      self->v->append(*PyBlitzArrayCxx_AsBlitz<uint8_t,4>(frame));
    }
    Py_RETURN_NONE;
  BOB_CATCH_MEMBER("append", 0)
  }
  
  
  static auto s_close = bob::extension::FunctionDoc(
    "close",
    "Closes the current video stream and forces writing the trailer.",
    "After this point the video is finalized and cannot be written to anymore.",
    true
  )
  .add_prototype("")
  ;
  static PyObject* PyBobIoVideoWriter_Close(PyBobIoVideoWriterObject* self) {
  BOB_TRY
    self->v->close();
    Py_RETURN_NONE;
  BOB_CATCH_MEMBER("close", 0)
  }
  
  static PyMethodDef PyBobIoVideoWriter_Methods[] = {
      {
        s_append.name(),
        (PyCFunction)PyBobIoVideoWriter_Append,
        METH_VARARGS|METH_KEYWORDS,
        s_append.doc(),
      },
      {
        s_close.name(),
        (PyCFunction)PyBobIoVideoWriter_Close,
        METH_NOARGS,
        s_close.doc(),
      },
      {0}  /* Sentinel */
  };
  
  Py_ssize_t PyBobIoVideoWriter_Len(PyBobIoVideoWriterObject* self) {
    return self->v->numberOfFrames();
  }
  
  static PyMappingMethods PyBobIoVideoWriter_Mapping = {
      (lenfunc)PyBobIoVideoWriter_Len, //mp_lenght
      0, /* (binaryfunc)PyBobIoVideoWriter_GetItem, //mp_subscript */
      0  /* (objobjargproc)PyBobIoVideoWriter_SetItem //mp_ass_subscript */
  };
  
  PyTypeObject PyBobIoVideoWriter_Type = {
      PyVarObject_HEAD_INIT(0, 0)
      0
  };
  
  bool init_BobIoVideoWriter(PyObject* module){
  
    // initialize the writer
    PyBobIoVideoWriter_Type.tp_name = s_fullname;
    PyBobIoVideoWriter_Type.tp_basicsize = sizeof(PyBobIoVideoWriterObject);
    PyBobIoVideoWriter_Type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
    PyBobIoVideoWriter_Type.tp_doc = s_writer.doc();
  
    // set the functions
    PyBobIoVideoWriter_Type.tp_new = PyType_GenericNew;
    PyBobIoVideoWriter_Type.tp_init = reinterpret_cast<initproc>(PyBobIoVideoWriter_Init);
    PyBobIoVideoWriter_Type.tp_dealloc = reinterpret_cast<destructor>(PyBobIoVideoWriter_Delete);
    PyBobIoVideoWriter_Type.tp_methods = PyBobIoVideoWriter_Methods;
    PyBobIoVideoWriter_Type.tp_getset = PyBobIoVideoWriter_getseters;
  
    PyBobIoVideoWriter_Type.tp_str = reinterpret_cast<reprfunc>(PyBobIoVideoWriter_Print);
    PyBobIoVideoWriter_Type.tp_repr = reinterpret_cast<reprfunc>(PyBobIoVideoWriter_Repr);
    PyBobIoVideoWriter_Type.tp_as_mapping = &PyBobIoVideoWriter_Mapping;
  
    // check that everything is fine
    if (PyType_Ready(&PyBobIoVideoWriter_Type) < 0) return false;
  
    // add the type to the module
    Py_INCREF(&PyBobIoVideoWriter_Type);
    return PyModule_AddObject(module, "writer", (PyObject*)&PyBobIoVideoWriter_Type) >= 0;
  }
  