// STL includes #include #include #include // Linux includes #include #include #include #include #include #include // qt #include #include #include #include #include // Local includes #include #include #include "Amvideocap.h" // Constants namespace { const bool verbose = false; const char DEFAULT_FB_DEVICE[] = "/dev/fb0"; const char DEFAULT_VIDEO_DEVICE[] = "/dev/amvideo"; const char DEFAULT_CAPTURE_DEVICE[] = "/dev/amvideocap0"; const int AMVIDEOCAP_WAIT_MAX_MS = 50; } //End of constants AmlogicGrabber::AmlogicGrabber() : Grabber("AMLOGICGRABBER") // Minimum required width or height is 160 , _captureDev(-1) , _videoDev(-1) , _lastError(0) , _fbGrabber(DEFAULT_FB_DEVICE) , _grabbingModeNotification(0) { _image_ptr = _image_bgr.memptr(); _useImageResampler = true; } AmlogicGrabber::~AmlogicGrabber() { closeDevice(_captureDev); closeDevice(_videoDev); } bool AmlogicGrabber::setupScreen() { bool rc (false); QSize screenSize = _fbGrabber.getScreenSize(DEFAULT_FB_DEVICE); if ( !screenSize.isEmpty() ) { if (setWidthHeight(screenSize.width(), screenSize.height())) { rc = _fbGrabber.setupScreen(); } } return rc; } bool AmlogicGrabber::openDevice(int &fd, const char* dev) { if (fd<0) { fd = ::open(dev, O_RDWR); } return fd >= 0; } void AmlogicGrabber::closeDevice(int &fd) { if (fd >= 0) { ::close(fd); fd = -1; } } bool AmlogicGrabber::isVideoPlaying() { bool rc = false; if(QFile::exists(DEFAULT_VIDEO_DEVICE)) { int videoDisabled = 1; if (!openDevice(_videoDev, DEFAULT_VIDEO_DEVICE)) { Error(_log, "Failed to open video device(%s): %d - %s", DEFAULT_VIDEO_DEVICE, errno, strerror(errno)); } else { // Check the video disabled flag if(ioctl(_videoDev, AMSTREAM_IOC_GET_VIDEO_DISABLE, &videoDisabled) < 0) { Error(_log, "Failed to retrieve video state from device: %d - %s", errno, strerror(errno)); closeDevice(_videoDev); } else { if ( videoDisabled == 0 ) { rc = true; } } } } return rc; } int AmlogicGrabber::grabFrame(Image & image) { int rc = 0; if (_isEnabled && !_isDeviceInError) { // Make sure video is playing, else there is nothing to grab if (isVideoPlaying()) { if (_grabbingModeNotification!=1) { Info(_log, "Switch to VPU capture mode"); _grabbingModeNotification = 1; _lastError = 0; } if (grabFrame_amvideocap(image) < 0) { closeDevice(_captureDev); rc = -1; } } else { if (_grabbingModeNotification!=2) { Info( _log, "Switch to Framebuffer capture mode"); _grabbingModeNotification = 2; _lastError = 0; } rc = _fbGrabber.grabFrame(image); //usleep(50 * 1000); } } return rc; } int AmlogicGrabber::grabFrame_amvideocap(Image & image) { int rc = 0; // If the device is not open, attempt to open it if (_captureDev < 0) { if (! openDevice(_captureDev, DEFAULT_CAPTURE_DEVICE)) { ErrorIf( _lastError != 1, _log,"Failed to open the AMLOGIC device (%d - %s):", errno, strerror(errno)); _lastError = 1; rc = -1; return rc; } } long r1 = ioctl(_captureDev, AMVIDEOCAP_IOW_SET_WANTFRAME_WIDTH, _width); long r2 = ioctl(_captureDev, AMVIDEOCAP_IOW_SET_WANTFRAME_HEIGHT, _height); long r3 = ioctl(_captureDev, AMVIDEOCAP_IOW_SET_WANTFRAME_AT_FLAGS, CAP_FLAG_AT_END); long r4 = ioctl(_captureDev, AMVIDEOCAP_IOW_SET_WANTFRAME_WAIT_MAX_MS, AMVIDEOCAP_WAIT_MAX_MS); if (r1<0 || r2<0 || r3<0 || r4<0 || _height==0 || _width==0) { ErrorIf(_lastError != 2,_log,"Failed to configure capture device (%d - %s)", errno, strerror(errno)); _lastError = 2; rc = -1; } else { int linelen = ((_width + 31) & ~31) * 3; size_t _bytesToRead = linelen * _height; // Read the snapshot into the memory ssize_t bytesRead = pread(_captureDev, _image_ptr, _bytesToRead, 0); if (bytesRead < 0) { int state; ioctl(_captureDev, AMVIDEOCAP_IOR_GET_STATE, &state); if (state == AMVIDEOCAP_STATE_ON_CAPTURE) { DebugIf(_lastError != 5, _log,"Video playback has been paused"); _lastError = 5; } else { ErrorIf(_lastError != 3, _log,"Read of device failed: %d - %s", errno, strerror(errno)); _lastError = 3; } rc = -1; } else { if (static_cast(_bytesToRead) != bytesRead) { // Read of snapshot failed ErrorIf(_lastError != 4, _log,"Capture failed to grab entire image [bytesToRead(%d) != bytesRead(%d)]", _bytesToRead, bytesRead); _lastError = 4; rc = -1; } else { _imageResampler.processImage(static_cast(_image_ptr), _width, _height, linelen, PixelFormat::BGR24, image); _lastError = 0; rc = 0; } } } return rc; } QJsonObject AmlogicGrabber::discover(const QJsonObject& params) { DebugIf(verbose, _log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData()); QJsonObject inputsDiscovered; if(QFile::exists(DEFAULT_VIDEO_DEVICE) && QFile::exists(DEFAULT_CAPTURE_DEVICE) ) { QJsonArray video_inputs; QSize screenSize = _fbGrabber.getScreenSize(); if ( !screenSize.isEmpty() ) { int fbIdx = _fbGrabber.getPath().rightRef(1).toInt(); DebugIf(verbose, _log, "FB device [%s] found with resolution: %dx%d", QSTRING_CSTR(_fbGrabber.getPath()), screenSize.width(), screenSize.height()); QJsonArray fps = { 1, 5, 10, 15, 20, 25, 30, 40, 50, 60 }; QJsonObject in; QString displayName; displayName = QString("Display%1").arg(fbIdx); in["name"] = displayName; in["inputIdx"] = fbIdx; QJsonArray formats; QJsonObject format; QJsonArray resolutionArray; QJsonObject resolution; resolution["width"] = screenSize.width(); resolution["height"] = screenSize.height(); resolution["fps"] = fps; resolutionArray.append(resolution); format["resolutions"] = resolutionArray; formats.append(format); in["formats"] = formats; video_inputs.append(in); } if (!video_inputs.isEmpty()) { inputsDiscovered["device"] = "amlogic"; inputsDiscovered["device_name"] = "AmLogic"; inputsDiscovered["type"] = "screen"; inputsDiscovered["video_inputs"] = video_inputs; } } if (inputsDiscovered.isEmpty()) { DebugIf(verbose, _log, "No displays found to capture from!"); } DebugIf(verbose, _log, "device: [%s]", QString(QJsonDocument(inputsDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData()); return inputsDiscovered; } void AmlogicGrabber::setVideoMode(VideoMode mode) { Grabber::setVideoMode(mode); _fbGrabber.setVideoMode(mode); } bool AmlogicGrabber::setPixelDecimation(int pixelDecimation) { return ( Grabber::setPixelDecimation( pixelDecimation) && _fbGrabber.setPixelDecimation( pixelDecimation)); } void AmlogicGrabber::setCropping(int cropLeft, int cropRight, int cropTop, int cropBottom) { Grabber::setCropping(cropLeft, cropRight, cropTop, cropBottom); _fbGrabber.setCropping(cropLeft, cropRight, cropTop, cropBottom); } bool AmlogicGrabber::setWidthHeight(int width, int height) { bool rc (false); if ( Grabber::setWidthHeight(width, height) ) { _image_bgr.resize(static_cast(width), static_cast(height)); _width = width; _height = height; _bytesToRead = _image_bgr.size(); _image_ptr = _image_bgr.memptr(); rc = _fbGrabber.setWidthHeight(width, height); } return rc; } bool AmlogicGrabber::setFramerate(int fps) { return (Grabber::setFramerate(fps) && _fbGrabber.setFramerate(fps)); }