How to pass the image preview from the QML Camera component to a C++ plugin

I'm writing an Ubuntu Touch QML app that captures and processes images from a camera device. The processing is done by a C++ plugin to which I'm passing the URL of the image.

I can successfully save images and load them from the C++ plugin, but I'd rather not have to save the images on the filesystem first, and pass the in-memory camera preview to the plugin instead.

Given the URL for the preview that I'm getting from the camera (image://camera/preview_1), I'm guessing I should be able to get a reference to the "camera" image provider and then use the requestImage() method to get the image.

However, I've not been able to figure out how to get hold of the camera image provider. Here's the code:

QString Decoder::decodeImageQML(const QUrl &imgUrl)
{
    /* 
     * @imgUrl: URL to the camera preview. E.g. image://camera/preview_1
     */

    QQmlEngine * engine;
    QQuickImageProvider *imageProvider = 0;

    // FIXME: this does not work, I'm not sure how to get hold of 
    // the image provider
    imageProvider = engine->imageProvider("camera");

    QImage image = imageProvider->requestImage(("preview_1", &size, QSize());

    return Decoder::decodeImage(image);
}

How do I get a reference to the camera image provider?


You can get valid engine instance using following syntax.

QQmlEngine *engine = QQmlEngine::contextForObject(this)->engine();

And to get pointer of image provider, you can do something like following,

QQmlImageProviderBase* imageProviderBase = engine->imageProvider(item.host());
QQuickImageProvider* imageProvider = static_cast<QQuickImageProvider*>(imageProviderBase);

As QQuickImageProvider is derived from QQmlImageProviderBase, I think this cast is ok. Not sure how otherwise we can get pointer of QQuickImageProvider from engine.


To get the preview image in C++, you should pass the mediaObject property of the QML Camera element to C++. You can see that code in the camera-app as example.

You could try to use the QCameraImageCapture with that mediaObject. But I'm not sure if that will work. But it has the imageCaptured signal you'll need. That has a QImage with the preview.

void AdvancedCamera::setCamera(QObject *cameraObject)
{
    QVariant cameraVariant = cameraObject->property("mediaObject");
    QCamera *camera = qvariant_cast<QCamera*>(cameraVariant);
    QCameraImageCapture *imageCapture = new QCameraImageCapture(camera);
    QObject::connect(imageCapture, SIGNAL(imageCaptured(int, const QImage&))
        myObject, SLOT(processPreview(int, const QImage&)));
}

If it doesn't work, here is another way, using the QCameraImageCaptureControl of the QMediaService:

void AdvancedCamera::setCamera(QObject *cameraObject)
{
    QVariant cameraVariant = cameraObject->property("mediaObject");
    QCamera *camera = qvariant_cast<QCamera*>(cameraVariant);
    QMediaService *service = camera->service();
    QMediaControl *control = service->requestControl(QCameraImageCaptureControl_iid);
    QCameraImageCaptureControl *captureControl = qobject_cast<QCameraImageCaptureControl*>(control);
    QObject::connect(captureControl, SIGNAL(imageCaptured(int, const QImage&))
        myObject, SLOT(processPreview(int, const QImage&)));
}

You should look a the code of the camera-app for a better example.

For getting the final image delivered as QVideoFrame in C++, you would need to set the captureDestination. But that's not (yet?) supported by the camera backend of the Ubuntu phones.