diff --git a/bob/pad/face/database/__init__.py b/bob/pad/face/database/__init__.py
index b842785c16887e19c0bc016b5ff249b53ab98a19..3a831f7fbd1335f23dceca7be79e501d41be12a4 100644
--- a/bob/pad/face/database/__init__.py
+++ b/bob/pad/face/database/__init__.py
@@ -1,4 +1,3 @@
-from .database import VideoPadFile
 from .database import VideoPadSample  # noqa: F401
 from .casiafasd import CasiaFasdPadDatabase
 from .casiasurf import CasiaSurfPadDatabase
@@ -26,7 +25,6 @@ def __appropriate__(*args):
 
 
 __appropriate__(
-    VideoPadFile,
     ReplayAttackPadDatabase,
     ReplayMobilePadDatabase,
     MaskAttackPadDatabase,
diff --git a/bob/pad/face/database/database.py b/bob/pad/face/database/database.py
index 475d957bb29e71f67663047419240670798fd31f..c8954f17a61e6203013f0a49aafefec21970f59f 100644
--- a/bob/pad/face/database/database.py
+++ b/bob/pad/face/database/database.py
@@ -2,12 +2,15 @@ from functools import partial
 import os
 from bob.pad.base.database import PadFile
 import bob.bio.video
-import bob.io.video
 from bob.db.base.annotations import read_annotation_file
 from sklearn.preprocessing import FunctionTransformer
 from bob.bio.video import VideoAsArray
 from bob.pipelines import DelayedSample
 
+from ..utils import frames, number_of_frames
+
+def get_no_transform(x):
+    return None
 
 def delayed_video_load(
     samples,
@@ -19,9 +22,7 @@ def delayed_video_load(
     get_transform=None,
     keep_extension_for_annotation=False,
 ):
-    if get_transform is None:
-        def get_transform(x):
-            return None
+    get_transform = get_transform or get_no_transform
 
     original_directory = original_directory or ""
     annotation_directory = annotation_directory or ""
@@ -81,119 +82,3 @@ def VideoPadSample(
             keep_extension_for_annotation=keep_extension_for_annotation,
         ),
     )
-
-
-class VideoPadFile(PadFile):
-    """A simple base class that defines basic properties of File object for the
-    use in face PAD experiments.
-    """
-
-    def __init__(
-        self,
-        attack_type,
-        client_id,
-        path,
-        file_id=None,
-        original_directory=None,
-        original_extension=".avi",
-        annotation_directory=None,
-        annotation_extension=None,
-        annotation_type=None,
-        selection_style=None,
-        max_number_of_frames=None,
-        step_size=None,
-    ):
-        super().__init__(
-            attack_type=attack_type,
-            client_id=client_id,
-            path=path,
-            file_id=file_id,
-            original_directory=original_directory,
-            original_extension=original_extension,
-            annotation_directory=annotation_directory,
-            annotation_extension=annotation_extension,
-            annotation_type=annotation_type,
-        )
-        self.selection_style = selection_style or "all"
-        self.max_number_of_frames = max_number_of_frames
-        self.step_size = step_size
-
-    def load(
-        self,
-    ):
-        """Loads the video file and returns in a `bob.bio.video.FrameContainer`.
-
-        Returns
-        -------
-        :any:`bob.bio.video.VideoAsArray`
-            The loaded frames inside a frame container.
-        """
-        path = self.make_path(self.original_directory, self.original_extension)
-        video = bob.bio.video.VideoAsArray(
-            path,
-            selection_style=self.selection_style,
-            max_number_of_frames=self.max_number_of_frames,
-            step_size=self.step_size,
-        )
-        return video
-
-    @property
-    def frames(self):
-        """Returns an iterator of frames in the video.
-        If your database video files need to be loaded in a special way, you need to
-        override this property.
-
-        Returns
-        -------
-        collections.abc.Iterator
-            An iterator returning frames of the video.
-        """
-        path = self.make_path(
-            directory=self.original_directory, extension=self.original_extension
-        )
-        return iter(bob.io.video.reader(path))
-
-    @property
-    def number_of_frames(self):
-        path = self.make_path(
-            directory=self.original_directory, extension=self.original_extension
-        )
-        return bob.io.video.reader(path).number_of_frames
-
-    @property
-    def frame_shape(self):
-        """Returns the size of each frame in this database.
-        This implementation assumes all frames have the same shape.
-        It's best to override this method in your database implementation and return
-        a constant.
-
-        Returns
-        -------
-        (int, int, int)
-            The (Channels, Height, Width) sizes.
-        """
-        path = self.make_path(
-            directory=self.original_directory, extension=self.original_extension
-        )
-        frame = next(bob.io.video.reader(path))
-        return frame.shape
-
-    @property
-    def annotations(self):
-        """Reads the annotations
-        For this property to work, you need to set ``annotation_directory``,
-        ``annotation_extension``, and ``annotation_type`` attributes of the files when
-        database's object method is called.
-
-        Returns
-        -------
-        dict
-            The annotations as a dictionary.
-        """
-        if self.annotation_directory is None:
-            return None
-
-        annotation_file = self.make_path(
-            directory=self.annotation_directory, extension=self.annotation_extension
-        )
-        return read_annotation_file(annotation_file, self.annotation_type)
diff --git a/bob/pad/face/database/replay_mobile.py b/bob/pad/face/database/replay_mobile.py
index 46d121cb022cf8e16dfd4057b5c815206f9e80bc..08f6f7fd7e2faf7ecb5b66e5f02ad71f9d8e269a 100644
--- a/bob/pad/face/database/replay_mobile.py
+++ b/bob/pad/face/database/replay_mobile.py
@@ -16,10 +16,9 @@ def get_rm_video_transform(sample):
     should_flip = sample.should_flip
 
     def transform(video):
-        video = np.asarray(video)
-        video = np.rollaxis(video, -1, -2)
-        if should_flip:
-            video = video[..., ::-1, :]
+        if not should_flip:
+            # after changing to imageio-ffmpeg, we need to flip other way around
+            video = video[..., ::-1]
         return video
 
     return transform
diff --git a/bob/pad/face/test/test_databases.py b/bob/pad/face/test/test_databases.py
index 3c5fba90acc6ebcace6565f44155d21a0fb55af9..0683529a1034e167b5c0013c23671cfbb9b9d106 100644
--- a/bob/pad/face/test/test_databases.py
+++ b/bob/pad/face/test/test_databases.py
@@ -5,6 +5,7 @@
 from nose.plugins.skip import SkipTest
 
 import bob.bio.base
+import numpy as np
 
 
 def test_replayattack():
@@ -51,8 +52,8 @@ def test_replayattack():
             "nose": [152, 164],
         }
         assert sample.data.shape == (20, 3, 240, 320)
-        assert sample.data[0][0, 0, 0] == 8
-    except RuntimeError as e:
+        np.testing.assert_equal(sample.data[0][:, 0, 0], [8, 9, 11])
+    except (RuntimeError, FileNotFoundError) as e:
         raise SkipTest(e)
 
 
@@ -76,22 +77,49 @@ def test_replaymobile():
         len(database.samples(groups=["train", "dev", "eval"], purposes="attack")) == 640
     )
 
-    sample = database.sort(database.samples())[0]
+    all_samples = database.sort(database.samples())
+    sample = all_samples[0]
+    assert (
+        sample.key
+        == "devel/attack/attack_client005_session01_mattescreen_fixed_mobile_photo_lightoff.mov"
+    ), sample.key
+    assert sample.should_flip
+    annot = dict(sample.annotations["0"])
+    assert annot["leye"][1] > annot["reye"][1], annot
+    assert annot == {
+        "bottomright": [760, 498],
+        "topleft": [374, 209],
+        "leye": [518, 417],
+        "reye": [522, 291],
+        "mouthleft": [669, 308],
+        "mouthright": [666, 407],
+        "nose": [585, 358],
+    }, annot
+
+    sample2 = [s for s in all_samples if not s.should_flip][0]
+    assert (
+        sample2.key
+        == "devel/attack/attack_client005_session01_mattescreen_fixed_tablet_photo_lightoff.mov"
+    ), sample2.key
+    assert not sample2.should_flip
+    annot = dict(sample2.annotations["0"])
+    assert annot["leye"][1] > annot["reye"][1], annot
+    assert annot == {
+        "reye": [873, 305],
+        "leye": [879, 423],
+        "nose": [937, 365],
+        "mouthleft": [1018, 313],
+        "mouthright": [1023, 405],
+        "topleft": [747, 226],
+        "bottomright": [1111, 495],
+    }, annot
+
     try:
-        annot = dict(sample.annotations["0"])
-        assert annot["leye"][1] > annot["reye"][1], annot
-        assert annot == {
-            "bottomright": [760, 498],
-            "topleft": [374, 209],
-            "leye": [518, 417],
-            "reye": [522, 291],
-            "mouthleft": [669, 308],
-            "mouthright": [666, 407],
-            "nose": [585, 358],
-        }
-        assert sample.data.shape == (20, 3, 720, 1280)
-        assert sample.data[0][0, 0, 0] == 13
-    except RuntimeError as e:
+        assert sample.data.shape == (20, 3, 1280, 720), sample.data.shape
+        np.testing.assert_equal(sample.data[0][:, 0, 0], [13, 13, 13])
+        assert sample2.data.shape == (20, 3, 1280, 720), sample2.data.shape
+        np.testing.assert_equal(sample2.data[0][:, 0, 0], [19, 33, 30])
+    except (RuntimeError, FileNotFoundError) as e:
         raise SkipTest(e)
 
 
@@ -226,9 +254,9 @@ def test_swan():
             "reye": [510, 265],
             "topleft": [301, 169],
         }
-        assert sample.data.shape == (20, 3, 720, 1280)
-        assert sample.data[0][0, 0, 0] == 87
-    except RuntimeError as e:
+        assert sample.data.shape == (20, 3, 1280, 720)
+        np.testing.assert_equal(sample.data[0][:, 0, 0], [255, 255, 253])
+    except (RuntimeError, FileNotFoundError) as e:
         raise SkipTest(e)
 
 
@@ -285,6 +313,6 @@ def test_oulunpu():
             "topleft": [632, 394],
         }
         assert sample.data.shape == (20, 3, 1920, 1080)
-        assert sample.data[0][0, 0, 0] == 195
-    except RuntimeError as e:
+        np.testing.assert_equal(sample.data[0][:, 0, 0], [195, 191, 199])
+    except (RuntimeError, FileNotFoundError) as e:
         raise SkipTest(e)
diff --git a/bob/pad/face/test/test_utils.py b/bob/pad/face/test/test_utils.py
index b01674bf63d0508a3015483c05e5768e3f455435..609b109b374fa9a566c05528bc21499be79f1195 100644
--- a/bob/pad/face/test/test_utils.py
+++ b/bob/pad/face/test/test_utils.py
@@ -1,7 +1,8 @@
 from bob.pad.face.test.dummy.database import DummyDatabase as Database
-from bob.pad.face.utils import yield_faces, scale_face, blocks
+from bob.pad.face.utils import yield_faces, scale_face, blocks, frames, number_of_frames
 from nose.tools import raises
 import numpy
+import imageio
 
 
 def get_pad_sample(none_annotations=False):
@@ -12,6 +13,26 @@ def get_pad_sample(none_annotations=False):
 image = get_pad_sample().data[0]
 
 
+def test_video_frames():
+    # get the path to cockatoo.mp4 from imageio-ffmpeg
+    path = imageio.core.Request("imageio:cockatoo.mp4", "r").get_local_filename()
+    # read 2 frames
+    for i, frame in enumerate(frames(path)):
+        assert frame.shape == (3, 720, 1280), frame.shape
+        assert frame.ndim == 3, frame.ndim
+        if i == 0:
+            numpy.testing.assert_equal(frame[:, 0, 0], [116, 119, 104])
+        elif i == 1:
+            numpy.testing.assert_equal(frame[:, 0, 0], [116, 119, 104])
+        else:
+            break
+
+    # test number of frames
+    n_frames = number_of_frames(path)
+    assert n_frames == 280, n_frames
+
+
+
 def dummy_cropper(frame, annotations=None):
     return frame
 
diff --git a/bob/pad/face/utils/load_utils.py b/bob/pad/face/utils/load_utils.py
index 2de8ec74ed6e7ab0b33c8a98fbb6c35cafe65f57..e2c3cb6aa465954a2d154cf192713b279d937576 100644
--- a/bob/pad/face/utils/load_utils.py
+++ b/bob/pad/face/utils/load_utils.py
@@ -1,6 +1,6 @@
 from bob.bio.face.annotator import min_face_size_validator
 from bob.bio.video.annotator import normalize_annotations
-from bob.io.video import reader
+from imageio import get_reader
 from bob.ip.base import scale, block, block_output_shape, block_generator
 from bob.ip.color import rgb_to_yuv, rgb_to_hsv
 from bob.ip.facedetect import bounding_box_from_annotation
@@ -8,6 +8,7 @@ from collections import OrderedDict
 from functools import partial
 import numpy
 import random
+import bob.io.image
 
 
 def frames(path):
@@ -23,8 +24,9 @@ def frames(path):
     numpy.ndarray
         A frame of the video. The size is (3, 240, 320).
     """
-    video = reader(path)
-    return iter(video)
+    video = get_reader(path)
+    for frame in video:
+        yield bob.io.image.to_bob(frame)
 
 
 def number_of_frames(path):
@@ -40,8 +42,8 @@ def number_of_frames(path):
     int
         The number of frames. Then, it yields the frames.
     """
-    video = reader(path)
-    return video.number_of_frames
+    video = get_reader(path)
+    return video.count_frames()
 
 
 def bbx_cropper(frame, annotations):
diff --git a/conda/meta.yaml b/conda/meta.yaml
index 0bd0aac83cde4ad1f7a5b1c07a71d009754f5d3c..d00a6c4b8bf256d9a1cfad13beb6f92f2f296fa4 100644
--- a/conda/meta.yaml
+++ b/conda/meta.yaml
@@ -34,12 +34,14 @@ requirements:
     - numpy {{ numpy }}
     - scikit-learn {{ scikit_learn }}
     - scikit-image {{ scikit_image }}
+    - imageio-ffmpeg {{ imageio_ffmpeg }}
   run:
     - python
     - setuptools
     - {{ pin_compatible('numpy') }}
     - {{ pin_compatible('scikit-learn', min_pin='x.x') }}
     - {{ pin_compatible('scikit-image') }}
+    - {{ pin_compatible('imageio-ffmpeg') }}
 
 test:
   imports:
diff --git a/develop.cfg b/develop.cfg
index f6d74885e13cd213f3e7980ff16954986c39f6f3..cc3b6d7dec3253d08561b8f79377998c6d999762 100644
--- a/develop.cfg
+++ b/develop.cfg
@@ -4,66 +4,14 @@
 [buildout]
 parts = scripts
 eggs = bob.pad.face
-       bob.extension
-       bob.blitz
-       bob.core
-       bob.sp
-       bob.math
-       bob.io.base
-       bob.ip.gabor
-       bob.measure
-       bob.ip.base
-       bob.learn.boosting
-       bob.io.image
-       bob.ip.draw
-       bob.ip.color
-       bob.io.video
-       bob.io.matlab
-       bob.ip.flandmark
-       bob.ip.facedetect
-       bob.ip.dlib
-       bob.ip.qualitymeasure
-       bob.learn.linear
-       bob.db.base
-       bob.learn.em
-       bob.db.atnt
-       bob.bio.base
-       bob.bio.face
        bob.bio.video
-       bob.pad.base
 
 extensions = bob.buildout
              mr.developer
 
 auto-checkout = *
 
-develop = src/bob.extension
-          src/bob.blitz
-          src/bob.core
-          src/bob.sp
-          src/bob.math
-          src/bob.io.base
-          src/bob.ip.gabor
-          src/bob.measure
-          src/bob.ip.base
-          src/bob.learn.boosting
-          src/bob.io.image
-          src/bob.ip.draw
-          src/bob.ip.color
-          src/bob.io.video
-          src/bob.io.matlab
-          src/bob.ip.flandmark
-          src/bob.ip.facedetect
-          src/bob.ip.dlib
-          src/bob.ip.qualitymeasure
-          src/bob.learn.linear
-          src/bob.db.base
-          src/bob.learn.em
-          src/bob.db.atnt
-          src/bob.bio.base
-          src/bob.bio.face
-          src/bob.bio.video
-          src/bob.pad.base
+develop = src/bob.bio.video
           .
 
 ; options for bob.buildout
@@ -72,33 +20,7 @@ verbose = true
 newest = false
 
 [sources]
-bob.extension = git git@gitlab.idiap.ch:bob/bob.extension
-bob.blitz = git git@gitlab.idiap.ch:bob/bob.blitz
-bob.core = git git@gitlab.idiap.ch:bob/bob.core
-bob.sp = git git@gitlab.idiap.ch:bob/bob.sp
-bob.math = git git@gitlab.idiap.ch:bob/bob.math
-bob.io.base = git git@gitlab.idiap.ch:bob/bob.io.base
-bob.ip.gabor = git git@gitlab.idiap.ch:bob/bob.ip.gabor
-bob.measure = git git@gitlab.idiap.ch:bob/bob.measure
-bob.ip.base = git git@gitlab.idiap.ch:bob/bob.ip.base
-bob.learn.boosting = git git@gitlab.idiap.ch:bob/bob.learn.boosting
-bob.io.image = git git@gitlab.idiap.ch:bob/bob.io.image
-bob.ip.draw = git git@gitlab.idiap.ch:bob/bob.ip.draw
-bob.ip.color = git git@gitlab.idiap.ch:bob/bob.ip.color
-bob.io.video = git git@gitlab.idiap.ch:bob/bob.io.video
-bob.io.matlab = git git@gitlab.idiap.ch:bob/bob.io.matlab
-bob.ip.flandmark = git git@gitlab.idiap.ch:bob/bob.ip.flandmark
-bob.ip.facedetect = git git@gitlab.idiap.ch:bob/bob.ip.facedetect
-bob.ip.dlib = git git@gitlab.idiap.ch:bob/bob.ip.dlib
-bob.ip.qualitymeasure = git git@gitlab.idiap.ch:bob/bob.ip.qualitymeasure
-bob.learn.linear = git git@gitlab.idiap.ch:bob/bob.learn.linear
-bob.db.base = git git@gitlab.idiap.ch:bob/bob.db.base
-bob.learn.em = git git@gitlab.idiap.ch:bob/bob.learn.em
-bob.db.atnt = git git@gitlab.idiap.ch:bob/bob.db.atnt
-bob.bio.base = git git@gitlab.idiap.ch:bob/bob.bio.base
-bob.bio.face = git git@gitlab.idiap.ch:bob/bob.bio.face
 bob.bio.video = git git@gitlab.idiap.ch:bob/bob.bio.video
-bob.pad.base = git git@gitlab.idiap.ch:bob/bob.pad.base
 
 
 [scripts]
diff --git a/doc/api.rst b/doc/api.rst
index 8bdbd73c39b09dfc3840c1a10acb946bffba6bd8..4cb67e5c07feaa5afa11765386e426320b7ed403 100644
--- a/doc/api.rst
+++ b/doc/api.rst
@@ -12,12 +12,6 @@ This section lists all the functionality available in this library allowing to r
 Database Interfaces
 ------------------------------
 
-Base classes
-============
-
-.. autoclass:: bob.pad.face.database.VideoPadFile
-
-
 REPLAY-ATTACK Database
 ========================
 
diff --git a/requirements.txt b/requirements.txt
index 6817dcb93d45874599ff7a903f35a63ac8c59820..c0e85a1e8a73e312c6a7a13bef3f77d51518cef5 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -12,3 +12,4 @@ bob.ip.color
 bob.ip.qualitymeasure
 scikit-learn
 scikit-image
+imageio-ffmpeg