17、Kanzi插件——2D插件官方示例

本文解析了如何在Kanzi中使用VideoView2D插件,通过mf_sample_grabber实现视频帧采样回调,配合video_controller的播放控制,展示了节点消息机制和属性编辑。重点介绍了VideoView2D类的实现,包括消息类型、属性和关键函数如play/pause/rewind操作。

一、示例效果

 

二、代码结构

代码结构如下图

前两个文件mf_sample_grabber.cpp和mf_sample_grabber.hpp实现了一个回调类;主要的作用是:当对视频进行帧采样时,将采样结果送回至UI线程(主线程)进行画面像素的刷新

文件video_controller.h定义了一个接口类,包括play,pause,stop等播放动作,而在video_util.h和video_util_mf.cpp对上述接口进行了具体的定义和实现

最后,在插件代码中分别video_view2d.cpp和video_view2d.hpp中利用前几个文件创建出来的类进行具体的逻辑处理

 

三、Kanzi插件代码分析

头文件的基本结构如下

typedef shared_ptr<VideoView2D> VideoView2DSharedPtr;

class NODE2D_PLUGIN_API VideoView2D : public Node2D
{
public:
    /// Represents message arguments of VideoView2D message types.
    class VideoNavigationMessageArguments: public MessageArguments
    {
    public:
        KZ_MESSAGE_ARGUMENTS_METACLASS_BEGIN(VideoNavigationMessageArguments, MessageArguments, "Video navigation message arguments")
        KZ_METACLASS_END()
    };

    static MessageType<VideoNavigationMessageArguments> VideoPlayMessage;
    static MessageType<VideoNavigationMessageArguments> VideoPauseMessage;
    static MessageType<VideoNavigationMessageArguments> VideoRewindMessage;
    //三个动作对应的消息
    
    static PropertyType<string> VideoFileNameProperty;//video文件名的属性
    static VideoView2DSharedPtr create(Domain* domain, string_view name);

    KZ_METACLASS_BEGIN(VideoView2D, Node2D, "VideoView2D")
        KZ_METACLASS_PROPERTY_TYPE(VideoFileNameProperty)//将消息和属性添加至容器
        KZ_METACLASS_MESSAGE_TYPE(VideoPlayMessage)
        KZ_METACLASS_MESSAGE_TYPE(VideoPauseMessage)
        KZ_METACLASS_MESSAGE_TYPE(VideoRewindMessage)
    KZ_METACLASS_END()

    static PropertyTypeEditorInfoSharedPtr makeEditorInfo();

protected:
    explicit VideoView2D(Domain* domain, string_view name);

    void initialize();
    virtual void onAttached() KZ_OVERRIDE;
    virtual void onDetached() KZ_OVERRIDE;

    virtual Vector2 measureOverride(Vector2 /*availableSize*/) KZ_OVERRIDE;
    virtual void updateRenderOverride() KZ_OVERRIDE;
    virtual void renderForegroundOverride(Renderer3D& renderer, 
        CompositionStack& compositionStack, const Matrix3x3& baseTransform) KZ_OVERRIDE;

    void onVideoPlay(VideoNavigationMessageArguments& message);
    void onVideoPause(VideoNavigationMessageArguments& message);
    void onVideoRewind(VideoNavigationMessageArguments& message);

private:
    /// Image texture which is used to display video frame.
    TextureSharedPtr m_imageTexture;
    /// Instance of video to play.
    VideoControllerSharedPtr m_video;
};

cpp文件中的代码如下

PropertyTypeEditorInfoSharedPtr VideoView2D::makeEditorInfo()
{
    return PropertyTypeEditorInfoSharedPtr(
        KZ_PROPERTY_TYPE_EDITOR_INFO(
            []() -> PropertyTypeEditorInfo::AttributeDictionary {
        PropertyTypeEditorInfo::AttributeDictionary dict;
        dict.displayName = "Video View 2D";//自定义节点的名称
        dict["FrequentlyAddedProperties"] = "Node2D.ForegroundBrush";//添加常用属性

        return dict;
    }()));
}

MessageType<VideoView2D::VideoNavigationMessageArguments> VideoView2D::VideoPlayMessage(kzMakeFixedString("Message.VideoView2D.Play"), 0);
MessageType<VideoView2D::VideoNavigationMessageArguments> VideoView2D::VideoPauseMessage(kzMakeFixedString("Message.VideoView2D.Pause"), 0);
MessageType<VideoView2D::VideoNavigationMessageArguments> VideoView2D::VideoRewindMessage(kzMakeFixedString("Message.VideoView2D.Rewind"), 0);

PropertyType<string> VideoView2D::VideoFileNameProperty(kzMakeFixedString("VideoView2D.VideoFileName"), "", 0, false,
                                                        KZ_DECLARE_EDITOR_METADATA
                                                        (
                                                            metadata.displayName = "Video Filename";
                                                            metadata.tooltip = "Video file to be played";
                                                            metadata.host = "VideoView2D:freq";
                                                        ));

VideoView2DSharedPtr VideoView2D::create(Domain* domain, string_view name)
{
    VideoView2DSharedPtr videoView2D = make_polymorphic_shared_ptr<Node>(new VideoView2D(domain, name));
    videoView2D->initialize();
    return videoView2D;
}

void VideoView2D::initialize()
{
    // Initialize the base class.
    Node2D::initialize();

    // Register message handlers.
    addMessageHandler(VideoPlayMessage, this, &VideoView2D::onVideoPlay);//三个消息处理动作,直接调用play pause等函数
    addMessageHandler(VideoPauseMessage, this, &VideoView2D::onVideoPause);
    addMessageHandler(VideoRewindMessage, this, &VideoView2D::onVideoRewind);
}

VideoView2D::VideoView2D(Domain* domain, string_view name):
                         Node2D(domain, name)
{
    setRenderType(RenderTypeTexture);//将渲染形状设置为四边形
//Node will render itself as textured quad.
}

void VideoView2D::onAttached()//节点添加的动作
{
    using std::swap;
    Domain* domain = getDomain();
    Node2D::onAttached();

    BrushSharedPtr foregroundBrush;
#ifdef ANDROID
    foregroundBrush = acquireResource<Brush>(ResourceID("kzb://node2d_plugin/Brushes/VideoViewBrushAndroid"));
#else
    foregroundBrush = acquireResource<Brush>(ResourceID("kzb://node2d_plugin/Brushes/VideoViewBrush"));
#endif
    setForegroundBrush(foregroundBrush);//加载并设置前景画刷

    // Create new video.
    string videoFilename = getProperty(VideoFileNameProperty);
    VideoView2DSubmitFunctor task(*domain->getTaskDispatcher(), *this);
    VideoControllerSharedPtr video = VideoUtil::create(*domain, videoFilename, task);//创建一个video管理对象

    if (video) {//video可用的话,直接交换给全局成员变量
        m_imageTexture = video->getRenderTexture(domain, this->shared_from_this());
        swap(m_video, video);
    }
    else {
        kzLogWarning(KZ_LOG_CATEGORY_GENERIC, ("Error in \'{}\' video view 2d: failed to load \'{}\' video file", getName(), videoFilename));
    }
}

void VideoView2D::onDetached()//节点销毁的动作
{
    Node2D::onDetached();

    // Delete previous instance of video.
    m_imageTexture.reset();
    m_video.reset();
}
//******************************************************
void VideoView2D::renderForegroundOverride(//video view节点的渲染
    Renderer3D& renderer, CompositionStack& /*compositionStack*/, const Matrix3x3& baseTransform)
{
    if (m_video && m_visual) {
        m_video->onRendering();//这里通过mf_sample_grabber.cpp的回调类来获取视频帧的数据,然后将数据写入到渲染纹理
        m_visual->renderForeground(renderer, baseTransform);//具体的渲染动作函数
    }
}

Vector2 VideoView2D::measureOverride(Vector2 /*availableSize*/)//通过video的width和height计算自定义2D节点的size并返回
{
    Vector2 nodeSize;

    if (m_video && m_imageTexture) {
        // Get the node size based on the size of the texture.
        if (m_imageTexture) {
            nodeSize.setX(static_cast<float>(m_video->getWidth()));
            nodeSize.setY(static_cast<float>(m_video->getHeight()));
        }
    }

    return nodeSize;
}

void VideoView2D::updateRenderOverride()//渲染前完成特定节点纹理的更新
{
    if (m_imageTexture) {
        updateContentTexture(m_imageTexture);//更新前景画笔的内容纹理。 
    }
}                                                                                                                

 

四、Kanzi工程的属性及触发设置

工程窗口的主要就是三个button,每个button设置一个buttonclick的动作,比如,rewind button点击时,会发一个rewind的消息,从而触发对应的消息处理函数,在消息处理函数中,最终会调用video_util类中的play,pause等动作

每个消息的作用目标都是插件自定义的的节点

 

参考

《Kanzi官方文档》

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值