mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-02 01:44:06 -04:00
WebcamVideoOpenCV
This commit is contained in:
parent
f6e2d9a387
commit
285923e83e
@ -17,6 +17,7 @@
|
|||||||
config_vision.h \
|
config_vision.h \
|
||||||
openCVTexture.I openCVTexture.h \
|
openCVTexture.I openCVTexture.h \
|
||||||
webcamVideo.h webcamVideo.I \
|
webcamVideo.h webcamVideo.I \
|
||||||
|
webcamVideoCursorOpenCV.h webcamVideoOpenCV.h \
|
||||||
webcamVideoCursorV4L.h webcamVideoV4L.h
|
webcamVideoCursorV4L.h webcamVideoV4L.h
|
||||||
|
|
||||||
#define INCLUDED_SOURCES \
|
#define INCLUDED_SOURCES \
|
||||||
@ -25,6 +26,8 @@
|
|||||||
openCVTexture.cxx \
|
openCVTexture.cxx \
|
||||||
webcamVideo.cxx \
|
webcamVideo.cxx \
|
||||||
webcamVideoDS.cxx \
|
webcamVideoDS.cxx \
|
||||||
|
webcamVideoCursorOpenCV.cxx \
|
||||||
|
webcamVideoOpenCV.cxx \
|
||||||
webcamVideoCursorV4L.cxx \
|
webcamVideoCursorV4L.cxx \
|
||||||
webcamVideoV4L.cxx
|
webcamVideoV4L.cxx
|
||||||
|
|
||||||
@ -32,6 +35,7 @@
|
|||||||
arToolKit.I arToolKit.h \
|
arToolKit.I arToolKit.h \
|
||||||
openCVTexture.I openCVTexture.h \
|
openCVTexture.I openCVTexture.h \
|
||||||
webcamVideo.h webcamVideo.I \
|
webcamVideo.h webcamVideo.I \
|
||||||
|
webcamVideoCursorOpenCV.h webcamVideoOpenCV.h \
|
||||||
webcamVideoCursorV4L.h webcamVideoV4L.h
|
webcamVideoCursorV4L.h webcamVideoV4L.h
|
||||||
|
|
||||||
#define IGATESCAN all
|
#define IGATESCAN all
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
#include "config_vision.h"
|
#include "config_vision.h"
|
||||||
#include "openCVTexture.h"
|
#include "openCVTexture.h"
|
||||||
#include "webcamVideo.h"
|
#include "webcamVideo.h"
|
||||||
|
#include "webcamVideoCursorOpenCV.h"
|
||||||
|
#include "webcamVideoOpenCV.h"
|
||||||
#include "webcamVideoCursorV4L.h"
|
#include "webcamVideoCursorV4L.h"
|
||||||
#include "webcamVideoV4L.h"
|
#include "webcamVideoV4L.h"
|
||||||
|
|
||||||
@ -52,6 +54,9 @@ init_libvision() {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_OPENCV
|
#ifdef HAVE_OPENCV
|
||||||
|
WebcamVideoCursorOpenCV::init_type();
|
||||||
|
WebcamVideoOpenCV::init_type();
|
||||||
|
|
||||||
OpenCVTexture::init_type();
|
OpenCVTexture::init_type();
|
||||||
OpenCVTexture::register_with_read_factory();
|
OpenCVTexture::register_with_read_factory();
|
||||||
|
|
||||||
|
@ -617,7 +617,7 @@ get_frame_data(int frame,
|
|||||||
y_pitch = -y_pitch;
|
y_pitch = -y_pitch;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (const unsigned char *)image->imageData;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
|
@ -2,7 +2,9 @@
|
|||||||
#include "config_vision.cxx"
|
#include "config_vision.cxx"
|
||||||
#include "openCVTexture.cxx"
|
#include "openCVTexture.cxx"
|
||||||
#include "webcamVideo.cxx"
|
#include "webcamVideo.cxx"
|
||||||
|
#include "webcamVideoCursorOpenCV.cxx"
|
||||||
#include "webcamVideoCursorV4L.cxx"
|
#include "webcamVideoCursorV4L.cxx"
|
||||||
#include "webcamVideoDS.cxx"
|
#include "webcamVideoDS.cxx"
|
||||||
|
#include "webcamVideoOpenCV.cxx"
|
||||||
#include "webcamVideoV4L.cxx"
|
#include "webcamVideoV4L.cxx"
|
||||||
|
|
||||||
|
@ -59,6 +59,11 @@ find_all_webcams() {
|
|||||||
extern void find_all_webcams_v4l();
|
extern void find_all_webcams_v4l();
|
||||||
find_all_webcams_v4l();
|
find_all_webcams_v4l();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_OPENCV
|
||||||
|
extern void find_all_webcams_opencv();
|
||||||
|
find_all_webcams_opencv();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
|
177
panda/src/vision/webcamVideoCursorOpenCV.cxx
Normal file
177
panda/src/vision/webcamVideoCursorOpenCV.cxx
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
// Filename: webcamVideoCursorOpenCV.cxx
|
||||||
|
// Created by: drose (20Oct10)
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// PANDA 3D SOFTWARE
|
||||||
|
// Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||||
|
//
|
||||||
|
// All use of this software is subject to the terms of the revised BSD
|
||||||
|
// license. You should have received a copy of this license along
|
||||||
|
// with this source code in a file named "LICENSE."
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "webcamVideoOpenCV.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_OPENCV
|
||||||
|
|
||||||
|
#include "pStatTimer.h"
|
||||||
|
|
||||||
|
TypeHandle WebcamVideoCursorOpenCV::_type_handle;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: WebcamVideoCursorOpenCV::Constructor
|
||||||
|
// Access: Published
|
||||||
|
// Description:
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
WebcamVideoCursorOpenCV::
|
||||||
|
WebcamVideoCursorOpenCV(WebcamVideoOpenCV *src) : MovieVideoCursor(src) {
|
||||||
|
_size_x = src->_size_x;
|
||||||
|
_size_y = src->_size_y;
|
||||||
|
_num_components = 3;
|
||||||
|
_length = 1.0E10;
|
||||||
|
_can_seek = false;
|
||||||
|
_can_seek_fast = false;
|
||||||
|
_aborted = false;
|
||||||
|
_last_start = -1.0;
|
||||||
|
_next_start = 0.0;
|
||||||
|
_streaming = true;
|
||||||
|
_ready = false;
|
||||||
|
|
||||||
|
_capture = cvCaptureFromCAM(src->_camera_index);
|
||||||
|
if (_capture != NULL) {
|
||||||
|
_size_x = (int)cvGetCaptureProperty(_capture, CV_CAP_PROP_FRAME_WIDTH);
|
||||||
|
_size_y = (int)cvGetCaptureProperty(_capture, CV_CAP_PROP_FRAME_HEIGHT);
|
||||||
|
_ready = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: WebcamVideoCursorOpenCV::Destructor
|
||||||
|
// Access: Published, Virtual
|
||||||
|
// Description:
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
WebcamVideoCursorOpenCV::
|
||||||
|
~WebcamVideoCursorOpenCV() {
|
||||||
|
if (_capture != NULL) {
|
||||||
|
cvReleaseCapture(&_capture);
|
||||||
|
_capture = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: WebcamVideoCursorOpenCV::fetch_into_buffer
|
||||||
|
// Access: Published, Virtual
|
||||||
|
// Description:
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
void WebcamVideoCursorOpenCV::
|
||||||
|
fetch_into_buffer(double time, unsigned char *block, bool bgra) {
|
||||||
|
if (!_ready) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char *dest = block;
|
||||||
|
int num_components = bgra ? 4 : 3;
|
||||||
|
ssize_t dest_x_pitch = num_components; // Assume component_width == 1
|
||||||
|
ssize_t dest_y_pitch = _size_x * dest_x_pitch;
|
||||||
|
|
||||||
|
const unsigned char *r, *g, *b;
|
||||||
|
ssize_t x_pitch, y_pitch;
|
||||||
|
if (get_frame_data(r, g, b, x_pitch, y_pitch)) {
|
||||||
|
if (num_components == 3 && x_pitch == 3) {
|
||||||
|
// The easy case--copy the whole thing in, row by row.
|
||||||
|
ssize_t copy_bytes = _size_x * dest_x_pitch;
|
||||||
|
nassertv(copy_bytes <= dest_y_pitch && copy_bytes <= abs(y_pitch));
|
||||||
|
|
||||||
|
for (int y = 0; y < _size_y; ++y) {
|
||||||
|
memcpy(dest, r, copy_bytes);
|
||||||
|
dest += dest_y_pitch;
|
||||||
|
r += y_pitch;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// The harder case--interleave in the color channels, pixel by
|
||||||
|
// pixel.
|
||||||
|
|
||||||
|
for (int y = 0; y < _size_y; ++y) {
|
||||||
|
ssize_t dx = 0;
|
||||||
|
ssize_t sx = 0;
|
||||||
|
for (int x = 0; x < _size_x; ++x) {
|
||||||
|
dest[dx] = r[sx];
|
||||||
|
dest[dx + 1] = g[sx];
|
||||||
|
dest[dx + 2] = b[sx];
|
||||||
|
dx += dest_x_pitch;
|
||||||
|
sx += x_pitch;
|
||||||
|
}
|
||||||
|
dest += dest_y_pitch;
|
||||||
|
r += y_pitch;
|
||||||
|
g += y_pitch;
|
||||||
|
b += y_pitch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: WebcamVideoCursorOpenCV::get_frame_data
|
||||||
|
// Access: Private
|
||||||
|
// Description: Gets the data needed to traverse through the
|
||||||
|
// decompressed buffer. Returns true on success, false
|
||||||
|
// on failure.
|
||||||
|
//
|
||||||
|
// In the case of a success indication (true return
|
||||||
|
// value), the three pointers r, g, b are loaded with
|
||||||
|
// the addresses of the three components of the
|
||||||
|
// bottom-left pixel of the image. (They will be
|
||||||
|
// adjacent in memory in the case of an interleaved
|
||||||
|
// image, and separated in the case of a
|
||||||
|
// separate-channel image.) The x_pitch value is filled
|
||||||
|
// with the amount to add to each pointer to advance to
|
||||||
|
// the pixel to the right; and the y_pitch value is
|
||||||
|
// filled with the amount to add to each pointer to
|
||||||
|
// advance to the pixel above. Note that these values
|
||||||
|
// may be negative (particularly in the case of a
|
||||||
|
// top-down image).
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
bool WebcamVideoCursorOpenCV::
|
||||||
|
get_frame_data(const unsigned char *&r,
|
||||||
|
const unsigned char *&g,
|
||||||
|
const unsigned char *&b,
|
||||||
|
ssize_t &x_pitch, ssize_t &y_pitch) {
|
||||||
|
nassertr(ready(), false);
|
||||||
|
|
||||||
|
IplImage *image = cvQueryFrame(_capture);
|
||||||
|
if (image == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = (const unsigned char *)image->imageData;
|
||||||
|
g = r + 1;
|
||||||
|
b = g + 1;
|
||||||
|
x_pitch = 3;
|
||||||
|
y_pitch = image->widthStep;
|
||||||
|
|
||||||
|
if (image->dataOrder == 1) {
|
||||||
|
// Separate channel images. That means a block of r, followed by
|
||||||
|
// a block of g, followed by a block of b.
|
||||||
|
x_pitch = 1;
|
||||||
|
g = r + image->height * y_pitch;
|
||||||
|
b = g + image->height * y_pitch;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (image->origin == 0) {
|
||||||
|
// The image data starts with the top row and ends with the bottom
|
||||||
|
// row--the opposite of Texture::_ram_data's storage convention.
|
||||||
|
// Therefore, we must increment the initial pointers to the last
|
||||||
|
// row, and count backwards.
|
||||||
|
r += (image->height - 1) * y_pitch;
|
||||||
|
g += (image->height - 1) * y_pitch;
|
||||||
|
b += (image->height - 1) * y_pitch;
|
||||||
|
y_pitch = -y_pitch;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
64
panda/src/vision/webcamVideoCursorOpenCV.h
Normal file
64
panda/src/vision/webcamVideoCursorOpenCV.h
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
// Filename: webcamVideoCursorOpenCV.h
|
||||||
|
// Created by: drose (20Oct10)
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// PANDA 3D SOFTWARE
|
||||||
|
// Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||||
|
//
|
||||||
|
// All use of this software is subject to the terms of the revised BSD
|
||||||
|
// license. You should have received a copy of this license along
|
||||||
|
// with this source code in a file named "LICENSE."
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef WEBCAMVIDEOCURSOROPENCV_H
|
||||||
|
#define WEBCAMVIDEOCURSOROPENCV_H
|
||||||
|
|
||||||
|
#include "pandabase.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_OPENCV
|
||||||
|
|
||||||
|
#include "webcamVideo.h"
|
||||||
|
|
||||||
|
class WebcamVideoOpenCV;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Class : WebcamVideoCursorOpenCV
|
||||||
|
// Description : The Video4Linux implementation of webcams.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
class WebcamVideoCursorOpenCV : public MovieVideoCursor {
|
||||||
|
public:
|
||||||
|
WebcamVideoCursorOpenCV(WebcamVideoOpenCV *src);
|
||||||
|
virtual ~WebcamVideoCursorOpenCV();
|
||||||
|
virtual void fetch_into_buffer(double time, unsigned char *block, bool rgba);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool get_frame_data(const unsigned char *&r,
|
||||||
|
const unsigned char *&g,
|
||||||
|
const unsigned char *&b,
|
||||||
|
ssize_t &x_pitch, ssize_t &y_pitch);
|
||||||
|
|
||||||
|
CvCapture *_capture;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static TypeHandle get_class_type() {
|
||||||
|
return _type_handle;
|
||||||
|
}
|
||||||
|
static void init_type() {
|
||||||
|
MovieVideoCursor::init_type();
|
||||||
|
register_type(_type_handle, "WebcamVideoCursorOpenCV",
|
||||||
|
MovieVideoCursor::get_class_type());
|
||||||
|
}
|
||||||
|
virtual TypeHandle get_type() const {
|
||||||
|
return get_class_type();
|
||||||
|
}
|
||||||
|
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static TypeHandle _type_handle;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // HAVE_OPENCV
|
||||||
|
|
||||||
|
#endif
|
70
panda/src/vision/webcamVideoOpenCV.cxx
Normal file
70
panda/src/vision/webcamVideoOpenCV.cxx
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// Filename: webcamVideoOpenCV.cxx
|
||||||
|
// Created by: drose (20Oct10)
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// PANDA 3D SOFTWARE
|
||||||
|
// Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||||
|
//
|
||||||
|
// All use of this software is subject to the terms of the revised BSD
|
||||||
|
// license. You should have received a copy of this license along
|
||||||
|
// with this source code in a file named "LICENSE."
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "webcamVideoOpenCV.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_OPENCV
|
||||||
|
|
||||||
|
#include "webcamVideoCursorOpenCV.h"
|
||||||
|
#include "configVariableInt.h"
|
||||||
|
|
||||||
|
TypeHandle WebcamVideoOpenCV::_type_handle;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: find_all_webcams_opencv
|
||||||
|
// Access: Public, Static
|
||||||
|
// Description: Finds all OpenCV webcams and adds them to the global
|
||||||
|
// list _all_webcams.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
void
|
||||||
|
find_all_webcams_opencv() {
|
||||||
|
// OpenCV doesn't really provide a way to enumerate cameras. We ask
|
||||||
|
// the user to do this via a config variable.
|
||||||
|
static ConfigVariableInt wemcam_opencv_camera_index
|
||||||
|
("webcam-opencv-camera-index", "0",
|
||||||
|
PRC_DESC("Specify the space-separated list of integer camera index "
|
||||||
|
"numbers that are assumed to be available via OpenCV to the "
|
||||||
|
"WebcamVideo interface. The default camera index is 0. "
|
||||||
|
"Specify empty string if there are no available cameras."));
|
||||||
|
for (int i = 0; i < wemcam_opencv_camera_index.get_num_words(); ++i) {
|
||||||
|
PT(WebcamVideo) wc = new WebcamVideoOpenCV(wemcam_opencv_camera_index[i]);
|
||||||
|
WebcamVideoOpenCV::_all_webcams.push_back(wc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: WebcamVideoOpenCV::Constructor
|
||||||
|
// Access: Published, Virtual
|
||||||
|
// Description:
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
WebcamVideoOpenCV::
|
||||||
|
WebcamVideoOpenCV(int camera_index) :
|
||||||
|
_camera_index(camera_index)
|
||||||
|
{
|
||||||
|
ostringstream strm;
|
||||||
|
strm << "OpenCV webcam " << _camera_index;
|
||||||
|
set_name(strm.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: WebcamVideoOpenCV::open
|
||||||
|
// Access: Published, Virtual
|
||||||
|
// Description: Open this video, returning a MovieVideoCursor.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
PT(MovieVideoCursor) WebcamVideoOpenCV::
|
||||||
|
open() {
|
||||||
|
return new WebcamVideoCursorOpenCV(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
64
panda/src/vision/webcamVideoOpenCV.h
Normal file
64
panda/src/vision/webcamVideoOpenCV.h
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
// Filename: webcamVideoOpenCV.h
|
||||||
|
// Created by: drose (20Oct10)
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// PANDA 3D SOFTWARE
|
||||||
|
// Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||||
|
//
|
||||||
|
// All use of this software is subject to the terms of the revised BSD
|
||||||
|
// license. You should have received a copy of this license along
|
||||||
|
// with this source code in a file named "LICENSE."
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef WEBCAMVIDEOOPENCV_H
|
||||||
|
#define WEBCAMVIDEOOPENCV_H
|
||||||
|
|
||||||
|
#include "pandabase.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_OPENCV
|
||||||
|
|
||||||
|
#include "webcamVideo.h"
|
||||||
|
|
||||||
|
class WebcamVideoCursorOpenCV;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Class : WebcamVideoOpenCV
|
||||||
|
// Description : The OpenCV implementation of webcams. Probably won't
|
||||||
|
// be needed once we have a native webcam implementation
|
||||||
|
// for each Panda3D-supported platform. (So far, we're
|
||||||
|
// 2 for 3.)
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
class WebcamVideoOpenCV : public WebcamVideo {
|
||||||
|
private:
|
||||||
|
WebcamVideoOpenCV(int camera_index);
|
||||||
|
virtual PT(MovieVideoCursor) open();
|
||||||
|
|
||||||
|
private:
|
||||||
|
int _camera_index;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static TypeHandle get_class_type() {
|
||||||
|
return _type_handle;
|
||||||
|
}
|
||||||
|
static void init_type() {
|
||||||
|
WebcamVideo::init_type();
|
||||||
|
register_type(_type_handle, "WebcamVideoOpenCV",
|
||||||
|
WebcamVideo::get_class_type());
|
||||||
|
}
|
||||||
|
virtual TypeHandle get_type() const {
|
||||||
|
return get_class_type();
|
||||||
|
}
|
||||||
|
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static TypeHandle _type_handle;
|
||||||
|
|
||||||
|
friend class WebcamVideoCursorOpenCV;
|
||||||
|
friend void find_all_webcams_opencv();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // HAVE_OPENCV
|
||||||
|
|
||||||
|
#endif
|
Loading…
x
Reference in New Issue
Block a user