diff --git a/doc/c_cpp_api.rst b/doc/c_cpp_api.rst index 73a8583cf5e2350fd263cbb85baf5d7cb6677582..270e6455e657e19f64873067b556709bcbd857a7 100644 --- a/doc/c_cpp_api.rst +++ b/doc/c_cpp_api.rst @@ -51,6 +51,23 @@ Generic Functions Returns ``NPY_NOTYPE`` in case of problems, and sets a :py:class:`RuntimeError`. +.. cpp:function:: PyObject* PyBobIo_TypeInfoAsTuple (const bob::core::array::typeinfo& ti) + + Converts the ``bob::core::array::typeinfo&`` object into a **new reference** + to a :py:class:`tuple` with 3 elements: + + [0] + The data type as a :py:class:`numpy.dtype` object + + [1] + The shape of the object, as a tuple of integers + + [2] + The strides of the object, as a tuple of integers + + Returns ``0`` in case of failure, or a **new reference** to the tuple + described above in case of success. + Bob File Support ---------------- diff --git a/xbob/io/__init__.py b/xbob/io/__init__.py index 39f2bbefcc430590280ab787beb10b75fbf0a36d..294551a81194d83a2a6162eb362370dfb7f1ba30 100644 --- a/xbob/io/__init__.py +++ b/xbob/io/__init__.py @@ -1,4 +1,4 @@ -from ._library import __version__, __api_version__, File +from ._library import __version__, __api_version__, File, VideoReader from . import _externals import os diff --git a/xbob/io/externals.cpp b/xbob/io/externals.cpp index a08ffe4654b4cfaa93d0bcff9b78f755a2e86538..0f71821de70bcd7e6fc025ac1bef7bfb803406b4 100644 --- a/xbob/io/externals.cpp +++ b/xbob/io/externals.cpp @@ -15,7 +15,6 @@ #include <boost/preprocessor/stringize.hpp> #include <boost/format.hpp> -#include <bob/config.h> #include <bob/io/CodecRegistry.h> #include <bob/io/VideoUtilities.h> diff --git a/xbob/io/file.cpp b/xbob/io/file.cpp index 70b5c20a823d4407de54fe51148ae1cc93071046..77ab0aab5de2c2ab16413f763c4325138c5b12f8 100644 --- a/xbob/io/file.cpp +++ b/xbob/io/file.cpp @@ -12,7 +12,6 @@ #include <numpy/arrayobject.h> #include <blitz.array/capi.h> #include <stdexcept> - #include <bobskin.h> #define FILETYPE_NAME file @@ -113,23 +112,11 @@ static PyObject* PyBobIoFile_Repr(PyBobIoFileObject* self) { } static PyObject* PyBobIoFile_Filename(PyBobIoFileObject* self) { - return -# if PY_VERSION_HEX >= 0x03000000 - PyUnicode_FromString -# else - PyString_FromString -# endif - (self->f->filename().c_str()); + return Py_BuildValue("s", self->f->filename().c_str()); } static PyObject* PyBobIoFile_CodecName(PyBobIoFileObject* self) { - return -# if PY_VERSION_HEX >= 0x03000000 - PyUnicode_FromString -# else - PyString_FromString -# endif - (self->f->name().c_str()); + return Py_BuildValue("s", self->f->name().c_str()); } PyDoc_STRVAR(s_filename_str, "filename"); @@ -234,11 +221,6 @@ static PyObject* PyBobIoFile_GetItem (PyBobIoFileObject* self, Py_ssize_t i) { bobskin skin(retval, info.dtype); self->f->read(skin, i); } - catch (std::runtime_error& e) { - if (!PyErr_Occurred()) PyErr_Format(PyExc_RuntimeError, "caught std::runtime_error while reading object #%" PY_FORMAT_SIZE_T "d from file `%s': %s", i, self->f->filename().c_str(), e.what()); - Py_DECREF(retval); - return 0; - } catch (std::exception& e) { if (!PyErr_Occurred()) PyErr_Format(PyExc_RuntimeError, "caught std::exception while reading object #%" PY_FORMAT_SIZE_T "d from file `%s': %s", i, self->f->filename().c_str(), e.what()); Py_DECREF(retval); @@ -423,11 +405,7 @@ static PyObject* PyBobIoFile_Append(PyBobIoFileObject* self, PyObject *args, PyO return 0; } -# if PY_VERSION_HEX >= 0x03000000 - return PyLong_FromSsize_t(pos); -# else - return PyInt_FromSsize_t(pos); -# endif + return Py_BuildValue("n", pos); } @@ -454,6 +432,35 @@ Returns the current position of the newly written array.\n\ " ); +PyObject* PyBobIo_TypeInfoAsTuple (const bob::core::array::typeinfo& ti) { + + int type_num = PyBobIo_AsTypenum(ti.dtype); + if (type_num == NPY_NOTYPE) return 0; + + PyObject* retval = Py_BuildValue("NNN", + reinterpret_cast<PyObject*>(PyArray_DescrFromType(type_num)), + PyTuple_New(ti.nd), //shape + PyTuple_New(ti.nd) //strides + ); + if (!retval) return 0; + + PyObject* shape = PyTuple_GET_ITEM(retval, 1); + PyObject* stride = PyTuple_GET_ITEM(retval, 2); + for (Py_ssize_t i=0; i<ti.nd; ++i) { + if (PyTuple_SetItem(shape, i, Py_BuildValue("n", ti.shape[i])) != 0) { + Py_DECREF(retval); + return 0; + } + if (PyTuple_SetItem(stride, i, Py_BuildValue("n", ti.stride[i])) != 0) { + Py_DECREF(retval); + return 0; + } + } + + return retval; + +} + static PyObject* PyBobIoFile_Describe(PyBobIoFileObject* self, PyObject *args, PyObject* kwds) { /* Parses input arguments in a single shot */ @@ -473,36 +480,7 @@ static PyObject* PyBobIoFile_Describe(PyBobIoFileObject* self, PyObject *args, P 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; - + return PyBobIo_TypeInfoAsTuple(*info); } PyDoc_STRVAR(s_describe_str, "describe"); diff --git a/xbob/io/include/xbob.io/api.h b/xbob/io/include/xbob.io/api.h index f1a8e6afa1e6d3d4c5b18028dab54e116f1c8f65..f29a09024156cfd7983045813f0bcab83ca6fba9 100644 --- a/xbob/io/include/xbob.io/api.h +++ b/xbob/io/include/xbob.io/api.h @@ -9,6 +9,7 @@ #define XBOB_IO_H #include <xbob.io/config.h> +#include <bob/config.h> #include <bob/io/File.h> #if WITH_FFMPEG @@ -57,6 +58,10 @@ typedef struct { #define PyBobIo_AsTypenum_RET int #define PyBobIo_AsTypenum_PROTO (bob::core::array::ElementType et) +#define PyBobIo_TypeInfoAsTuple_NUM 3 +#define PyBobIo_TypeInfoAsTuple_RET PyObject* +#define PyBobIo_TypeInfoAsTuple_PROTO (const bob::core::array::typeinfo& ti) + #if WITH_FFMPEG /****************** @@ -71,16 +76,16 @@ typedef struct { } PyBobIoVideoReaderObject; -#define PyBobIoVideoReader_Type_NUM 3 +#define PyBobIoVideoReader_Type_NUM 4 #define PyBobIoVideoReader_Type_TYPE PyTypeObject #endif /* WITH_FFMPEG */ /* Total number of C API pointers */ #if WITH_FFMPEG -# define PyXbobIo_API_pointers 4 +# define PyXbobIo_API_pointers 5 #else -# define PyXbobIo_API_pointers 3 +# define PyXbobIo_API_pointers 6 #endif /* WITH_FFMPEG */ #ifdef XBOB_IO_MODULE @@ -104,6 +109,8 @@ typedef struct { ************************/ PyBobIo_AsTypenum_RET PyBobIo_AsTypenum PyBobIo_AsTypenum_PROTO; + + PyBobIo_TypeInfoAsTuple_RET PyBobIo_TypeInfoAsTuple PyBobIo_TypeInfoAsTuple_PROTO; #if WITH_FFMPEG /****************** @@ -159,6 +166,8 @@ typedef struct { # define PyBobIo_AsTypenum (*(PyBobIo_AsTypenum_RET (*)PyBobIo_AsTypenum_PROTO) PyXbobIo_API[PyBobIo_AsTypenum_NUM]) +# define PyBobIo_TypeInfoAsTuple (*(PyBobIo_TypeInfoAsTuple_RET (*)PyBobIo_TypeInfoAsTuple_PROTO) PyXbobIo_API[PyBobIo_TypeInfoAsTuple_NUM]) + #if WITH_FFMPEG /****************** * Video bindings * diff --git a/xbob/io/main.cpp b/xbob/io/main.cpp index 58b855705fe38d0b484290a89a34983f539f3bb6..c24476a3c944975cb42fc29231de774b4d8e0ca4 100644 --- a/xbob/io/main.cpp +++ b/xbob/io/main.cpp @@ -72,6 +72,8 @@ PyMODINIT_FUNC ENTRY_FUNCTION(XBOB_IO_MODULE_NAME) (void) { PyXbobIo_API[PyBobIo_AsTypenum_NUM] = (void *)PyBobIo_AsTypenum; + PyXbobIo_API[PyBobIo_TypeInfoAsTuple_NUM] = (void *)PyBobIo_TypeInfoAsTuple; + #if WITH_FFMPEG /****************** * Video bindings * diff --git a/xbob/io/test/test_video.py b/xbob/io/test/test_video.py index 486c6dee8d2f7c1574a6eaaf053d8f0573ebf789..323b946ce7a64df4673de7af107534c123925e5e 100644 --- a/xbob/io/test/test_video.py +++ b/xbob/io/test/test_video.py @@ -61,6 +61,45 @@ def test_output_format_support(): for fmt in ('avi', 'mov', 'mp4'): assert fmt in supported +@testutils.ffmpeg_found() +def test_video_reader_attributes(): + + from .. import VideoReader + + iv = VideoReader(INPUT_VIDEO) + + assert isinstance(iv.filename, str) + assert isinstance(iv.height, int) + assert isinstance(iv.width, int) + assert iv.height != iv.width + assert isinstance(iv.duration, int) + assert isinstance(iv.format_name, str) + assert isinstance(iv.format_long_name, str) + assert isinstance(iv.codec_name, str) + assert isinstance(iv.codec_long_name, str) + assert isinstance(iv.frame_rate, float) + assert isinstance(iv.video_type, tuple) + assert len(iv.video_type) == 3 + assert isinstance(iv.video_type[0], numpy.dtype) + assert isinstance(iv.video_type[1], tuple) + assert isinstance(iv.video_type[2], tuple) + assert isinstance(iv.frame_type, tuple) + assert len(iv.frame_type) == 3 + assert iv.frame_type[0] == iv.video_type[0] + assert isinstance(iv.video_type[1], tuple) + nose.tools.eq_(len(iv.video_type[1]), len(iv.frame_type[1])+1) + nose.tools.eq_(len(iv.video_type[2]), len(iv.frame_type[2])+1) + assert isinstance(iv.info, str) + +@testutils.ffmpeg_found() +def test_video_reader_str(): + + from .. import VideoReader + + iv = VideoReader(INPUT_VIDEO) + assert repr(iv) + assert str(iv) + @testutils.ffmpeg_found() def test_can_use_array_interface(): diff --git a/xbob/io/videoreader.cpp b/xbob/io/videoreader.cpp index 51ab96d324f51dcd7c708eab05cfd73e05cdf6aa..fdb2f4462e16662e6a560140c761936adbbc5b5d 100644 --- a/xbob/io/videoreader.cpp +++ b/xbob/io/videoreader.cpp @@ -10,6 +10,10 @@ #if WITH_FFMPEG #include <boost/make_shared.hpp> +#include <numpy/arrayobject.h> +#include <blitz.array/capi.h> +#include <stdexcept> +#include <bobskin.h> #define VIDEOREADER_NAME VideoReader PyDoc_STRVAR(s_videoreader_str, BOOST_PP_STRINGIZE(XBOB_IO_MODULE_PREFIX) "." BOOST_PP_STRINGIZE(VIDEOREADER_NAME)); @@ -101,6 +105,423 @@ varying between 0 and 255, with zero meaning pure black and 255,\n\ pure white (color).\n\ "); +PyObject* PyBobIoVideoReader_Filename(PyBobIoVideoReaderObject* self) { + return Py_BuildValue("s", self->v->filename().c_str()); +} + +PyDoc_STRVAR(s_filename_str, "filename"); +PyDoc_STRVAR(s_filename_doc, +"[str] The full path to the file that will be decoded by this object"); + +PyObject* PyBobIoVideoReader_Height(PyBobIoVideoReaderObject* self) { + return Py_BuildValue("n", self->v->height()); +} + +PyDoc_STRVAR(s_height_str, "height"); +PyDoc_STRVAR(s_height_doc, +"[int] The height of each frame in the video (a multiple of 2)"); + +PyObject* PyBobIoVideoReader_Width(PyBobIoVideoReaderObject* self) { + return Py_BuildValue("n", self->v->width()); +} + +PyDoc_STRVAR(s_width_str, "width"); +PyDoc_STRVAR(s_width_doc, +"[int] The width of each frame in the video (a multiple of 2)"); + +PyObject* PyBobIoVideoReader_Len(PyBobIoVideoReaderObject* self) { + return Py_BuildValue("n", self->v->numberOfFrames()); +} + +PyDoc_STRVAR(s_number_of_frames_str, "number_of_frames"); +PyDoc_STRVAR(s_number_of_frames_doc, +"[int] The number of frames in this video file"); + +PyObject* PyBobIoVideoReader_Duration(PyBobIoVideoReaderObject* self) { + return Py_BuildValue("n", self->v->duration()); +} + +PyDoc_STRVAR(s_duration_str, "duration"); +PyDoc_STRVAR(s_duration_doc, +"[int] Total duration of this video file in microseconds (long)"); + +PyObject* PyBobIoVideoReader_FormatName(PyBobIoVideoReaderObject* self) { + return Py_BuildValue("s", self->v->formatName().c_str()); +} + +PyDoc_STRVAR(s_format_name_str, "format_name"); +PyDoc_STRVAR(s_format_name_doc, +"[str] Short name of the format in which this video file was recorded in"); + +PyObject* PyBobIoVideoReader_FormatLongName(PyBobIoVideoReaderObject* self) { + return Py_BuildValue("s", self->v->formatLongName().c_str()); +} + +PyDoc_STRVAR(s_format_long_name_str, "format_long_name"); +PyDoc_STRVAR(s_format_long_name_doc, +"[str] Full name of the format in which this video file was recorded in"); + +PyObject* PyBobIoVideoReader_CodecName(PyBobIoVideoReaderObject* self) { + return Py_BuildValue("s", self->v->codecName().c_str()); +} + +PyDoc_STRVAR(s_codec_name_str, "codec_name"); +PyDoc_STRVAR(s_codec_name_doc, +"[str] Short name of the codec in which this video file was recorded in"); + +PyObject* PyBobIoVideoReader_CodecLongName(PyBobIoVideoReaderObject* self) { + return Py_BuildValue("s", self->v->codecLongName().c_str()); +} + +PyDoc_STRVAR(s_codec_long_name_str, "codec_long_name"); +PyDoc_STRVAR(s_codec_long_name_doc, +"[str] Full name of the codec in which this video file was recorded in"); + +PyObject* PyBobIoVideoReader_FrameRate(PyBobIoVideoReaderObject* self) { + return PyFloat_FromDouble(self->v->frameRate()); +} + +PyDoc_STRVAR(s_frame_rate_str, "frame_rate"); +PyDoc_STRVAR(s_frame_rate_doc, +"[float] Video's announced frame rate (note there are video formats with variable frame rates)"); + +PyObject* PyBobIoVideoReader_VideoType(PyBobIoVideoReaderObject* self) { + return PyBobIo_TypeInfoAsTuple(self->v->video_type()); +} + +PyDoc_STRVAR(s_video_type_str, "video_type"); +PyDoc_STRVAR(s_video_type_doc, +"[tuple] Typing information to load all of the file at once"); + +PyObject* PyBobIoVideoReader_FrameType(PyBobIoVideoReaderObject* self) { + return PyBobIo_TypeInfoAsTuple(self->v->frame_type()); +} + +PyDoc_STRVAR(s_frame_type_str, "frame_type"); +PyDoc_STRVAR(s_frame_type_doc, +"[tuple] Typing information to load each frame separatedly"); + +static PyObject* PyBobIoVideoReader_Print(PyBobIoVideoReaderObject* self) { + return Py_BuildValue("s", self->v->info().c_str()); +} + +PyDoc_STRVAR(s_info_str, "info"); +PyDoc_STRVAR(s_info_doc, +"[str] A string with lots of video information (same as ``str(x)``)"); + +static PyGetSetDef PyBobIoVideoReader_getseters[] = { + { + s_filename_str, + (getter)PyBobIoVideoReader_Filename, + 0, + s_filename_doc, + 0, + }, + { + s_height_str, + (getter)PyBobIoVideoReader_Height, + 0, + s_height_doc, + 0, + }, + { + s_width_str, + (getter)PyBobIoVideoReader_Width, + 0, + s_width_doc, + 0, + }, + { + s_number_of_frames_str, + (getter)PyBobIoVideoReader_Len, + 0, + s_number_of_frames_doc, + 0, + }, + { + s_duration_str, + (getter)PyBobIoVideoReader_Duration, + 0, + s_duration_doc, + 0, + }, + { + s_format_name_str, + (getter)PyBobIoVideoReader_FormatName, + 0, + s_format_name_doc, + 0, + }, + { + s_format_long_name_str, + (getter)PyBobIoVideoReader_FormatLongName, + 0, + s_format_long_name_doc, + 0, + }, + { + s_codec_name_str, + (getter)PyBobIoVideoReader_CodecName, + 0, + s_codec_name_doc, + 0, + }, + { + s_codec_long_name_str, + (getter)PyBobIoVideoReader_CodecLongName, + 0, + s_codec_long_name_doc, + 0, + }, + { + s_frame_rate_str, + (getter)PyBobIoVideoReader_FrameRate, + 0, + s_frame_rate_doc, + 0, + }, + { + s_video_type_str, + (getter)PyBobIoVideoReader_VideoType, + 0, + s_video_type_doc, + 0, + }, + { + s_frame_type_str, + (getter)PyBobIoVideoReader_FrameType, + 0, + s_frame_type_doc, + 0, + }, + { + s_info_str, + (getter)PyBobIoVideoReader_Print, + 0, + s_info_doc, + 0, + }, + {0} /* Sentinel */ +}; + +static PyObject* PyBobIoVideoReader_Repr(PyBobIoVideoReaderObject* self) { + return +# if PY_VERSION_HEX >= 0x03000000 + PyUnicode_FromFormat +# else + PyString_FromFormat +# endif + ("%s(filename='%s')", s_videoreader_str, self->v->filename().c_str()); +} + +/** + * If a keyboard interruption occurs, then it is translated into a C++ + * exception that makes the loop stops. + */ +static void Check_Interrupt() { + if(PyErr_CheckSignals() == -1) { + if (!PyErr_Occurred()) PyErr_SetInterrupt(); + throw std::runtime_error("error is already set"); + } +} + +static PyObject* PyBobIoFile_Load(PyBobIoVideoReaderObject* self, PyObject *args, PyObject* kwds) { + + /* Parses input arguments in a single shot */ + static const char* const_kwlist[] = {"raise_on_error", 0}; + static char** kwlist = const_cast<char**>(const_kwlist); + + PyObject* raise = 0; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &raise)) return 0; + + if (raise && !PyBool_Check(raise)) { + PyErr_SetString(PyExc_TypeError, "argument to `raise_on_error' must be a boolean"); + return 0; + } + + bool raise_on_error = false; + if (raise && (raise == Py_True)) raise_on_error = true; + + const bob::core::array::typeinfo& info = self->v->video_type(); + + npy_intp shape[NPY_MAXDIMS]; + for (int k=0; k<info.nd; ++k) shape[k] = info.shape[k]; + + int type_num = PyBobIo_AsTypenum(info.dtype); + if (type_num == NPY_NOTYPE) return 0; ///< failure + + PyObject* retval = PyArray_SimpleNew(info.nd, shape, type_num); + if (!retval) return 0; + + Py_ssize_t frames_read = 0; + + try { + bobskin skin(retval, info.dtype); + frames_read = self->v->load(skin, raise_on_error, &Check_Interrupt); + } + catch (std::exception& e) { + if (!PyErr_Occurred()) PyErr_Format(PyExc_RuntimeError, "caught std::exception while reading video from file `%s': %s", self->v->filename().c_str(), e.what()); + Py_DECREF(retval); + return 0; + } + catch (...) { + if (!PyErr_Occurred()) PyErr_Format(PyExc_RuntimeError, "caught unknown exception while reading video from file `%s'", self->v->filename().c_str()); + Py_DECREF(retval); + return 0; + } + + if (frames_read != shape[0]) { + //resize + shape[0] = frames_read; + PyArray_Dims newshape; + newshape.ptr = shape; + newshape.len = info.nd; + PyArray_Resize((PyArrayObject*)retval, &newshape, 1, NPY_ANYORDER); + } + + return retval; + +} + +PyDoc_STRVAR(s_load_str, "load"); +PyDoc_STRVAR(s_load_doc, +"x.load([raise_on_error=False] -> numpy.ndarray\n\ +\n\ +Loads all of the video stream in a numpy ndarray organized\n\ +in this way: (frames, color-bands, height, width). I'll dynamically\n\ +allocate the output array and return it to you.\n\ +\n\ +The flag ``raise_on_error``, which is set to ``False`` by default\n\ +influences the error reporting in case problems are found with the\n\ +video file. If you set it to ``True``, we will report problems\n\ +raising exceptions. If you either don't set it or set it to\n\ +``False``, we will truncate the file at the frame with problems\n\ +and will not report anything. It is your task to verify if the\n\ +number of frames returned matches the expected number of frames as\n\ +reported by the property ``number_of_frames`` (or ``len``) of this\n\ +object.\n\ +"); + +static PyMethodDef PyBobIoVideoReader_Methods[] = { + { + s_load_str, + (PyCFunction)PyBobIoFile_Load, + METH_VARARGS|METH_KEYWORDS, + s_load_doc, + }, + {0} /* Sentinel */ +}; + +static PyObject* PyBobIoVideoReader_GetItem (PyBobIoVideoReaderObject* self, Py_ssize_t i) { + + if (i < 0 || i >= self->v->numberOfFrames()) { + PyErr_Format(PyExc_IndexError, "video frame index out of range - `%s' only contains %" PY_FORMAT_SIZE_T "d frame(s)", self->v->filename().c_str(), self->v->numberOfFrames()); + return 0; + } + + const bob::core::array::typeinfo& info = self->v->frame_type(); + + npy_intp shape[NPY_MAXDIMS]; + for (int k=0; k<info.nd; ++k) shape[k] = info.shape[k]; + + int type_num = PyBobIo_AsTypenum(info.dtype); + if (type_num == NPY_NOTYPE) return 0; ///< failure + + PyObject* retval = PyArray_SimpleNew(info.nd, shape, type_num); + if (!retval) return 0; + + try { + auto it = self->v->begin(); + it += i; + bobskin skin(retval, info.dtype); + it.read(skin); + } + catch (std::exception& e) { + if (!PyErr_Occurred()) PyErr_Format(PyExc_RuntimeError, "caught std::exception while reading frame %" PY_FORMAT_SIZE_T "d from file `%s': %s", i, self->v->filename().c_str(), e.what()); + Py_DECREF(retval); + return 0; + } + catch (...) { + if (!PyErr_Occurred()) PyErr_Format(PyExc_RuntimeError, "caught unknown exception while reading frame #%" PY_FORMAT_SIZE_T "d from file `%s'", i, self->v->filename().c_str()); + Py_DECREF(retval); + return 0; + } + + return retval; + +} + +static PyObject* PyBobIoVideoReader_GetSlice (PyBobIoVideoReaderObject* self, Py_ssize_t lo, Py_ssize_t hi) { + + if (lo < 0 || lo >= self->v->numberOfFrames()) { + PyErr_Format(PyExc_IndexError, "video frame index out of range - `%s' only contains %" PY_FORMAT_SIZE_T "d frame(s)", self->v->filename().c_str(), self->v->numberOfFrames()); + return 0; + } + + if (hi < 0 || hi >= self->v->numberOfFrames()) { + PyErr_Format(PyExc_IndexError, "video frame index out of range - `%s' only contains %" PY_FORMAT_SIZE_T "d frame(s)", self->v->filename().c_str(), self->v->numberOfFrames()); + return 0; + } + + const bob::core::array::typeinfo& info = self->v->frame_type(); + + npy_intp shape[NPY_MAXDIMS]; + for (int k=0; k<info.nd; ++k) shape[k] = info.shape[k]; + + int type_num = PyBobIo_AsTypenum(info.dtype); + if (type_num == NPY_NOTYPE) return 0; ///< failure + + PyObject* retval = PyList_New(0); + if (!retval) return 0; + + PyObject* frame = 0; + + try { + auto it = self->v->begin(); + it += lo; + for (size_t i=lo; it.parent() && i<hi; ++i) { + Check_Interrupt(); + frame = PyArray_SimpleNew(info.nd, shape, type_num); + if (!frame) { + Py_DECREF(retval); + return 0; + } + bobskin skin(frame, info.dtype); + it.read(skin); + int ok = PyList_Append(retval, frame); + Py_DECREF(frame); + frame = 0; + if (ok != 0) { + Py_DECREF(retval); + return 0; + } + } + } + catch (std::exception& e) { + if (!PyErr_Occurred()) PyErr_Format(PyExc_RuntimeError, "caught std::exception while reading slice [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d[ from file `%s': %s", lo, hi, self->v->filename().c_str(), e.what()); + Py_XDECREF(frame); + Py_DECREF(retval); + return 0; + } + catch (...) { + if (!PyErr_Occurred()) PyErr_Format(PyExc_RuntimeError, "caught std::exception while reading slice [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d[ from file `%s'", lo, hi, self->v->filename().c_str()); + Py_XDECREF(frame); + Py_DECREF(retval); + return 0; + } + + return retval; + +} + +static PySequenceMethods PyBobIoVideoReader_Sequence = { + (lenfunc)PyBobIoVideoReader_Len, + 0, /* concat */ + 0, /* repeat */ + (ssizeargfunc)PyBobIoVideoReader_GetItem, + (ssizessizeargfunc)PyBobIoVideoReader_GetSlice +}; + PyTypeObject PyBobIoVideoReader_Type = { PyObject_HEAD_INIT(0) 0, /*ob_size*/ @@ -112,13 +533,13 @@ PyTypeObject PyBobIoVideoReader_Type = { 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ - 0, //(reprfunc)PyBobIoVideoReader_Repr, /*tp_repr*/ + (reprfunc)PyBobIoVideoReader_Repr, /*tp_repr*/ 0, /*tp_as_number*/ - 0, //&PyBobIoVideoReader_Sequence, /*tp_as_sequence*/ + &PyBobIoVideoReader_Sequence, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash */ 0, /*tp_call*/ - 0, //(reprfunc)PyBobIoVideoReader_Repr, /*tp_str*/ + (reprfunc)PyBobIoVideoReader_Print, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ @@ -130,54 +551,22 @@ PyTypeObject PyBobIoVideoReader_Type = { 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ - 0, //PyBobIoVideoReader_Methods, /* tp_methods */ + PyBobIoVideoReader_Methods, /* tp_methods */ 0, /* tp_members */ - 0, //PyBobIoVideoReader_getseters, /* tp_getset */ + 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 */ + (initproc)PyBobIoVideoReader_Init, /* tp_init */ 0, /* tp_alloc */ - PyBobIoVideoReader_New, /* tp_new */ + 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) **/