Run webcam pipeline and optionally record the video to the specified
        output file path.

        __NB__ The output file container is determined based on the extension
        of the output file path.  Supported containers are `avi` and `mp4`.  In
        either case, the video is encoded in MPEG4 format.

        Arguments
        ---------

         - `output_path`: Output file path.
         - `device_config`:
           * Configuration dictionary or a `pandas.Series` in the format of a
             row of a frame returned by `caps.get_device_configs()`.
           * If not provided, the GStreamer `autovideosrc` is used.
         - `bitrate`: Target encode bit rate in bits/second (default=350kB/s)

def run(self, output_path, device_config=None, bitrate=350 << 3 << 10,
            sink=None):
        '''
        Run webcam pipeline and optionally record the video to the specified
        output file path.

        __NB__ The output file container is determined based on the extension
        of the output file path.  Supported containers are `avi` and `mp4`.  In
        either case, the video is encoded in MPEG4 format.

        Arguments
        ---------

         - `output_path`: Output file path.
         - `device_config`:
           * Configuration dictionary or a `pandas.Series` in the format of a
             row of a frame returned by `caps.get_device_configs()`.
           * If not provided, the GStreamer `autovideosrc` is used.
         - `bitrate`: Target encode bit rate in bits/second (default=350kB/s)
        '''
        # Create GStreamer pipeline
        self.pipeline = Gst.Pipeline()

        # Create bus to get events from GStreamer pipeline
        self.bus = self.pipeline.get_bus()
        self.bus.add_signal_watch()
        self.bus.connect('message::error', self.on_error)

        # This is needed to make the video output in our DrawingArea:
        self.bus.enable_sync_message_emission()

        # Create GStreamer elements
        if device_config is None:
            self.src = Gst.ElementFactory.make('autovideosrc', 'source')
        else:
            self.src = get_video_source()
            device_key = get_video_device_key()
            self.src.set_property(device_key, device_config['device'])
        if sink is None:
            self.sink = Gst.ElementFactory.make('autovideosink', 'sink')
        else:
            self.sink = sink
        self.sink.set_property('sync', False)

        self.filter_ = Gst.ElementFactory.make('capsfilter', 'filter')
        tee = Gst.ElementFactory.make('tee', None)

        sink_queue = Gst.ElementFactory.make('queue', None)
        capture_queue = Gst.ElementFactory.make('queue', None)
        encoder = Gst.ElementFactory.make('avenc_mpeg4', None)
        encoder.set_property('bitrate', bitrate)
        encoder.set_property('bitrate-tolerance', 500 << 10)
        if path(output_path).ext.lower() == '.mp4':
            muxer = Gst.ElementFactory.make('mp4mux', None)
        elif path(output_path).ext.lower() == '.avi':
            muxer = Gst.ElementFactory.make('avimux', None)
        else:
            raise ValueError('Unsupported output file type: %s' %
                             path(output_path).ext)
        filesink = Gst.ElementFactory.make('filesink', None)
        filesink.set_property('location', output_path)

        videorate = Gst.ElementFactory.make('videorate', None)
        filter1 = Gst.ElementFactory.make('capsfilter', None)
        filter1.set_property('caps',
                             Gst.Caps('video/x-raw,framerate={framerate_numerator}/{framerate_denominator}'
                                      .format(**device_config)))

        caps = Gst.Caps(get_caps_str(device_config))
        self.filter_.set_property('caps', caps)

        src_elements = (self.src, self.filter_, videorate, filter1, tee)
        sink_elements = (sink_queue, self.sink)
        capture_elements = (capture_queue, encoder, muxer, filesink)

        # Add elements to the pipeline
        for d in src_elements + sink_elements + capture_elements:
            self.pipeline.add(d)

        for elements in (src_elements, sink_elements, capture_elements):
            for i, j in zip(elements[:-1], elements[1:]):
                i.link(j)

        tee.link(sink_elements[0])
        tee.link(capture_elements[0])


        self.output_path = output_path
        self.tee = tee
        self.muxer = muxer
        self.src_elements = src_elements
        self.sink_elements = sink_elements
        self.capture_elements = capture_elements

        self.pipeline.set_state(Gst.State.PLAYING)
        self._alive = True