// STL includes #include #include // Local includes #include "grabber/DispmanxFrameGrabber.h" DispmanxFrameGrabber::DispmanxFrameGrabber(const unsigned width, const unsigned height) : Grabber("DISPMANXGRABBER", width, height) , _vc_display(0) , _vc_resource(0) , _vc_flags(0) , _captureBuffer(new ColorRgba[0]) , _captureBufferSize(0) , _image_rgba(width, height) { _useImageResampler = false; // Initiase BCM bcm_host_init(); { // Check if the display can be opened and display the current resolution // Open the connection to the display _vc_display = vc_dispmanx_display_open(0); assert(_vc_display > 0); // Obtain the display information DISPMANX_MODEINFO_T vc_info; int result = vc_dispmanx_display_get_info(_vc_display, &vc_info); // Keep compiler happy in 'release' mode (void)result; assert(result == 0); Info(_log, "Display opened with resolution: %dx%d", vc_info.width, vc_info.height); // Close the displaye vc_dispmanx_display_close(_vc_display); } // Create the resources for capturing image uint32_t vc_nativeImageHandle; _vc_resource = vc_dispmanx_resource_create( VC_IMAGE_RGBA32, width, height, &vc_nativeImageHandle); assert(_vc_resource); // Define the capture rectangle with the same size vc_dispmanx_rect_set(&_rectangle, 0, 0, width, height); } DispmanxFrameGrabber::~DispmanxFrameGrabber() { delete[] _captureBuffer; // Clean up resources vc_dispmanx_resource_delete(_vc_resource); // De-init BCM bcm_host_deinit(); } void DispmanxFrameGrabber::setFlags(const int vc_flags) { _vc_flags = vc_flags; } int DispmanxFrameGrabber::grabFrame(Image & image) { int ret; // vc_dispmanx_resource_read_data doesn't seem to work well // with arbitrary positions so we have to handle cropping by ourselves unsigned cropLeft = _cropLeft; unsigned cropRight = _cropRight; unsigned cropTop = _cropTop; unsigned cropBottom = _cropBottom; if (_vc_flags & DISPMANX_SNAPSHOT_FILL) { // disable cropping, we are capturing the video overlay window cropLeft = cropRight = cropTop = cropBottom = 0; } unsigned imageWidth = _width - cropLeft - cropRight; unsigned imageHeight = _height - cropTop - cropBottom; // calculate final image dimensions and adjust top/left cropping in 3D modes switch (_videoMode) { case VIDEO_3DSBS: imageWidth /= 2; cropLeft /= 2; break; case VIDEO_3DTAB: imageHeight /= 2; cropTop /= 2; break; case VIDEO_2D: default: break; } // resize the given image if needed if (image.width() != imageWidth || image.height() != imageHeight) { image.resize(imageWidth, imageHeight); } if (_image_rgba.width() != imageWidth || _image_rgba.height() != imageHeight) { _image_rgba.resize(imageWidth, imageHeight); } // Open the connection to the display _vc_display = vc_dispmanx_display_open(0); if (_vc_display < 0) { Error(_log, "Cannot open display: %d", _vc_display); return -1; } // Create the snapshot (incl down-scaling) ret = vc_dispmanx_snapshot(_vc_display, _vc_resource, (DISPMANX_TRANSFORM_T) _vc_flags); if (ret < 0) { Error(_log, "Snapshot failed: %d", ret); vc_dispmanx_display_close(_vc_display); return ret; } // Read the snapshot into the memory void* imagePtr = _image_rgba.memptr(); void* capturePtr = imagePtr; unsigned imagePitch = imageWidth * sizeof(ColorRgba); // dispmanx seems to require the pitch to be a multiple of 64 unsigned capturePitch = (_rectangle.width * sizeof(ColorRgba) + 63) & (~63); // grab to temp buffer if image pitch isn't valid or if we are cropping if (imagePitch != capturePitch || (unsigned)_rectangle.width != imageWidth || (unsigned)_rectangle.height != imageHeight) { // check if we need to resize the capture buffer unsigned captureSize = capturePitch * _rectangle.height / sizeof(ColorRgba); if (_captureBufferSize != captureSize) { delete[] _captureBuffer; _captureBuffer = new ColorRgba[captureSize]; _captureBufferSize = captureSize; } capturePtr = &_captureBuffer[0]; } ret = vc_dispmanx_resource_read_data(_vc_resource, &_rectangle, capturePtr, capturePitch); if (ret < 0) { Error(_log, "vc_dispmanx_resource_read_data failed: %d", ret); vc_dispmanx_display_close(_vc_display); return ret; } // copy capture data to image if we captured to temp buffer if (imagePtr != capturePtr) { // adjust source pointer to top/left cropping uint8_t* src_ptr = (uint8_t*) capturePtr + cropLeft * sizeof(ColorRgba) + cropTop * capturePitch; for (unsigned y = 0; y < imageHeight; y++) { memcpy((uint8_t*)imagePtr + y * imagePitch, src_ptr + y * capturePitch, imagePitch); } } // Close the displaye vc_dispmanx_display_close(_vc_display); // image to output image _image_rgba.toRgb(image); return 0; }