Video Example Tutorial

This guide covers creating an ADTF filter that displays a 2D-Video inside the 3D-Scene. After reading this guide, you will know how to:

2D-Video Filter

The 2D-Video Filter receives and processes video data from an ADTF datfile and renders a video on a plane entity which can be placed inside the 3D-Scene.


cmake_minimum_required(VERSION 3.10.0)

set(EXAMPLE_NAME video_example)
set(PLUGIN_BINARY_OUTPUT_DIR "bin")

find_package(qt3ddisplay REQUIRED COMPONENTS base qmlbase)
find_package(Qt5 COMPONENTS Core OpenGL Quick Qml QuickWidgets 3DCore 3DExtras 3DRender 3DInput)

project(${EXAMPLE_NAME} VERSION 1.0.0)

set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)

# Adds the project to the Visual Studio solution, which when build
# creates a shared object called video_example.adtfplugin
adtf_add_filter(${EXAMPLE_NAME}
                ${EXAMPLE_NAME}_filter.cpp
                ${EXAMPLE_NAME}_filter.h
                qml/${EXAMPLE_NAME}_filter_resources.qrc
                qml/${EXAMPLE_NAME}_filter_main.qml
                )

set_target_properties(${EXAMPLE_NAME} PROPERTIES 
                AUTORCC true 
                AUTOUIC true)
                
target_link_libraries(${EXAMPLE_NAME} PRIVATE
                adtf::qt3d::base
                Qt5::3DRender
                )

# Adds the INSTALL project to the Visual Studio solution, which when build
# copies our filter to the subdirectory given as the second argument into ${PLUGIN_BINARY_OUTPUT_DIR}
adtf_install_filter(${EXAMPLE_NAME} ${PLUGIN_BINARY_OUTPUT_DIR})

set_target_properties(${EXAMPLE_NAME} PROPERTIES FOLDER examples/guides)

# Generates a plugindescription for our filter
adtf_create_plugindescription(
    TARGET
        ${EXAMPLE_NAME}
    PLUGIN_SUBDIR
        ${PLUGIN_BINARY_OUTPUT_DIR}
)

    

Now use the CMake-GUI to fill in all the information required by CMake to create the Visual Studio solution. Then open thevideo_exampleproject in the solution explorer.

The header file of the filter

Just like before we continue by creating our video_example_filter.h header file.

            
#pragma once

#include <qt3ddisplay/base/qt3d_base_filter.h>


// To implement a Filter which can be used in 3D-Display we subclass adtf::qt3d::cQt3DBaseFilter.
class cQt3DVideo : public adtf::qt3d::cQt3DBaseFilter
{

public:
    // This macros provides some meta information about our Implementation.
    // This will be exposed by the plugin class factory.
    ADTF_CLASS_ID_NAME(cQt3DVideo,
        "video.qt3d.cid",
        "Qt3D Video");

    // Constructor
    cQt3DVideo();

    // Destructor
    ~cQt3DVideo() = default;

private:
    // Define properties of the filter
    adtf::base::property_variable<tInt> m_oVideoWidth = 350;
    adtf::base::property_variable<tInt> m_oVideoHeight = 200;

    adtf::base::property_variable<tFloat64> m_oVideoPositionX = 0;
    adtf::base::property_variable<tFloat64> m_oVideoPositionY = 2;
    adtf::base::property_variable<tFloat64> m_oVideoPositionZ = 0;

    adtf::base::property_variable<tFloat64> m_oVideoRotationX = 90;
    adtf::base::property_variable<tFloat64> m_oVideoRotationY = -90;
    adtf::base::property_variable<tFloat64> m_oVideoRotationZ = 0;

    adtf::base::property_variable<tFloat64> m_oVideoScale = 0.01;
};


            
          

The source file of the filter

Now we need to write the video_example_filter.cpp source file.

            
#include "video_example_filter.h"

// For simplicity use the necessary namespaces.
using namespace adtf::ucom;
using namespace adtf::streaming;
using namespace adtf::services;
using namespace adtf::base;
using namespace adtf::mediadescription;
using namespace adtf::qt3d;

// The code behind the macro creates a plugin and the main entries to the plugin DLL or shared object.
// The cQt3DVideo class will be available through the plugins class factory.
ADTF_PLUGIN("Qt3D Video", cQt3DVideo)

// The main QML file gets loaded from the base class.
cQt3DVideo::cQt3DVideo() :
    cQt3DBaseFilter(":/modules/Qt3DVideo/video_example_filter_main.qml", false)
{
    // Register all properties
    RegisterPropertyVariable("Size/videoWidth", m_oVideoWidth);
    RegisterPropertyVariable("Size/videoHeight", m_oVideoHeight);

    RegisterPropertyVariable("Position/positionX", m_oVideoPositionX);
    RegisterPropertyVariable("Position/positionY", m_oVideoPositionY);
    RegisterPropertyVariable("Position/positionZ", m_oVideoPositionZ);

    RegisterPropertyVariable("Rotation/rotationX", m_oVideoRotationX);
    RegisterPropertyVariable("Rotation/rotationY", m_oVideoRotationY);
    RegisterPropertyVariable("Rotation/rotationZ", m_oVideoRotationZ);

    RegisterPropertyVariable("scale", m_oVideoScale);

    // Sets a short description for the component
    set_description(*this, "The video example filter creates a plane where a 2D video gets rendered.");

    // Set help link to jump to documentation from ADTF Configuration Editor
    set_help_link(*this, "../doc/guides/tutorial_video_example.html");
}
            
          

The QML file of the filter

Now we need to write the video_example_filter_main.qml qml file.

            
// Import the Qt libaries.
import QtQuick 2.12
import QtQuick.Scene2D 2.12

import Qt3D.Core 2.12
import Qt3D.Render 2.12
import Qt3D.Input 2.12
import Qt3D.Extras 2.12

import Adtf 1.0
import AdtfQt3DBase 1.0


Item 
{
    id: root

    // Retrieve values of all properties.
    property int videoWidth: filter.getProperty("Size/videoWidth")
    property int videoHeight: filter.getProperty("Size/videoHeight")

    property double videoPositionX: Qt3DHelperFunctions.convertToDouble(filter.getProperty("Position/positionX"))
    property double videoPositionY: Qt3DHelperFunctions.convertToDouble(filter.getProperty("Position/positionY"))
    property double videoPositionZ: Qt3DHelperFunctions.convertToDouble(filter.getProperty("Position/positionZ"))

    property double videoRotationX: Qt3DHelperFunctions.convertToDouble(filter.getProperty("Rotation/rotationX"))
    property double videoRotationY: Qt3DHelperFunctions.convertToDouble(filter.getProperty("Rotation/rotationY"))
    property double videoRotationZ: Qt3DHelperFunctions.convertToDouble(filter.getProperty("Rotation/rotationZ"))

    property double videoScale: Qt3DHelperFunctions.convertToDouble(filter.getProperty("scale"))

    // Use a property to store incoming data.
    property var incomingVideoData

    // Use the onCompleted handler to connect the filter to the incoming data and create an interface client.
    Component.onCompleted: {
        // Create an input pin.
        const inputVideo = filter.createInputPin("video")

        // Store incoming data.
        inputVideo.sample.connect((sample) => {incomingVideoData = sample.data})

        const qtshared = filter.createInterfaceClient("qtshared", "qtshared")

        // Set the parent of your implementation to 'rootEntity' of the 3D-Display.
        filter.connected.connect(function() {
            if (qtshared.connected) {
                video2dRootEntity.parent = qtshared.getObject("rootEntity")
            }
        })
    }

    // Use the onChanged handler of the 'incomingVideoData' property to trigger updates.
    onIncomingVideoDataChanged: {
        updateVideo(); 
    }

    Entity {
        id: video2dRootEntity

        // Set a name which is displayed in the VisualElementsView.
        objectName: "video2d"

        // Use a simple PlaneMesh.
        PlaneMesh {
            id: mesh
            width: root.videoWidth
            height: root.videoHeight
        }

        // Use the Transform component to apply all defined properties.
        Transform {
            id: transform
            translation: Qt.vector3d(root.videoPositionX, root.videoPositionY, root.videoPositionZ)
            rotationX: root.videoRotationX
            rotationY: root.videoRotationY
            rotationZ: root.videoRotationZ
            scale: root.videoScale
        }

        // Use the TextureMaterial which provides a default implementation of a simple unlit texture.
        TextureMaterial {
            id: material
            texture: videoTexture
        }

        // The Scene2D enables rendering qml into a texture, which can be used as a part of our 3D visualization.
        Scene2D {
            output: RenderTargetOutput {
                attachmentPoint: RenderTargetOutput.Color0
                texture: Texture2D {
                    id: videoTexture
                    width: root.videoWidth
                    height: root.videoHeight
                    format: Texture.RGBA8_UNorm
                }
            }

            // Here we can specify the rendered content.
            // The content is a combination of a rectangle which is used as background and the image of the video.
            Rectangle {
                id: videoBackground
                width: root.videoWidth
                height: root.videoHeight
                color: "black"

                ImageItem {
                    id: videoImageItem
                    image: root.incomingVideoData
                    width: parent.width
                    height: parent.height

                    // Rotate the image to your needs.
                    transform: Rotation { 
                        origin.x: root.videoPositionX + (videoImageItem.width / 2)
                        origin.y: root.videoPositionY + (videoImageItem.height / 2)
                        axis { x: 1; y: 0; z: 0 } angle: 180
                    }
                }
            }
        }

        components: [mesh, transform, material]
    }

    // Triggers the 'textureChanged' signal which forces a redraw of the corresponding Entity.
    function updateVideo() {
        material.textureChanged(videoTexture)
    }
}

            
          

The resources file of the filter

Now we need to write the video_example_filter_resources.qrc resources file. For detailed information about the Qt Resource System have a look at Qt Resources System.

            
<RCC>
    <qresource prefix="/modules/Qt3DVideo">
        <file>video_example_filter_main.qml</file>
    </qresource>
</RCC>

            
          

Finally we need to build the Project.

Project setup

Now we want to integrate and test our filters in an ADTF Project. We can accomplish this using the Configuration Editor. The following example also includes the Grid and PlayerControl Filter.

Video session

Finally, we can run the session and visualize our data.

Video example

Where to go next?

Have a look at the Sensor Visualization Example Filter.