当前位置: 首页 > 知识库问答 >
问题:

将非平凡的gstreamer“pad回调”返回为装箱闭包

戚哲
2023-03-14

我正在尝试编写一个工厂函数来创建闭包,用作gstreamer中的“pad回调”。我已经提供了一个精简的例子,它应该可以在安装了gstreamer机箱和gstreamer二进制文件/插件的情况下编译。

通过我的研究,我使用了“impl-trait”方法,而不是装箱,使工厂函数工作。不过,我想找出装箱方法,因为它在某些情况下似乎更合适。

这是我得到的最接近。通过使用“Box”取消标记为<code>Closure function的部分的注释,可以看出该问题

CtrlC或从stdin退出\n应该关闭程序。

extern crate gstreamer as gst;

use gst::prelude::*;
use std::io;

fn create_impl_probe_fn(
    x: i32,
) -> impl Fn(&gst::Pad, &mut gst::PadProbeInfo) -> gst::PadProbeReturn + Send + Sync + 'static {
    move |_, _| {
        println!("Idle... {}", x);

        gst::PadProbeReturn::Pass
    }
}

fn create_boxed_probe_fn(
    x: i32,
) -> Box<Fn(&gst::Pad, &mut gst::PadProbeInfo) -> gst::PadProbeReturn + Send + Sync + 'static> {
    Box::new(move |_, _| {
        println!("Idle... {}", x);

        gst::PadProbeReturn::Pass
    })
}

fn main() {
    println!("Starting...");
    //TODO Pass args to gst?
    gst::init().unwrap();

    //GStreamer
    let parse_line = "videotestsrc ! autovideosink name=mysink";

    let pipeline = gst::parse_launch(parse_line).unwrap();
    let ret = pipeline.set_state(gst::State::Playing);
    assert_ne!(ret, gst::StateChangeReturn::Failure);

    //Inline closure
    let mut x = 1;
    pipeline
        .clone()
        .dynamic_cast::<gst::Bin>()
        .unwrap()
        .get_by_name("mysink")
        .unwrap()
        .get_static_pad("sink")
        .unwrap()
        .add_probe(gst::PadProbeType::BLOCK, move |_, _| {
            println!("Idle... {}", x);

            gst::PadProbeReturn::Pass
        });

    //Closure function using 'impl'
    x = 20;
    let impl_probe_fn = create_impl_probe_fn(x);
    //TEMP Test
    pipeline
        .clone()
        .dynamic_cast::<gst::Bin>()
        .unwrap()
        .get_by_name("mysink")
        .unwrap()
        .get_static_pad("sink")
        .unwrap()
        .add_probe(gst::PadProbeType::BLOCK, impl_probe_fn);

    /*
    //Closure function using 'Box<>'
    x = 300;
    let boxed_probe_fn = create_boxed_probe_fn(x);
    //TEMP Test
    pipeline
        .clone()
        .dynamic_cast::<gst::Bin>()
        .unwrap()
        .get_by_name("mysink")
        .unwrap()
        .get_static_pad("sink")
        .unwrap()
        .add_probe(gst::PadProbeType::BLOCK, *boxed_probe_fn);
    */

    //Input Loop
    loop {
        let mut input = String::new();
        io::stdin().read_line(&mut input).unwrap();

        match input.trim() {
            "exit" => break,
            "info" => {
                let (state_change_return, cur_state, old_state) =
                    pipeline.get_state(gst::CLOCK_TIME_NONE);
                println!(
                    "Pipeline Info: {:?} {:?} {:?}",
                    state_change_return, cur_state, old_state
                );
            }
            "pause" => {
                let _ = pipeline.set_state(gst::State::Paused);
                println!("Pausing");
            }
            "resume" => {
                let _ = pipeline.set_state(gst::State::Playing);
                println!("Resuming");
            }
            _ => println!("Unrecognized command: '{}'", input.trim()),
        }

        println!("You've entered: {}", input.trim());
    }

    //Shutdown
    let ret = pipeline.set_state(gst::State::Null);
    assert_ne!(ret, gst::StateChangeReturn::Failure);
    println!("Shutting down streamer");
}

我知道网络上和SO上存在几个类似的问题,但我似乎不知道如何将任何解决方案应用于此特定功能。我在标题中包含了“非平凡”和“gstreamer”来区分。

[编辑]对不起,这里有更多的信息...只是不想把水弄混或使问题复杂化...

我不能发布我尝试过的所有内容。这是超过10个小时的小更改/尝试和许多标签。我可以复制一些看起来很接近的尝试,或者我期望有效的尝试。

上面的Box尝试是我根据这里的信息认为它会起作用的方式:https://doc.rust-lang.org/1.4.0/book/closures.html

https://doc.rust-lang.org/book/second-edition/ch19-05-advanced-functions-and-closures.html(这个没有关闭任何堆栈值,因此没有“移动”。)

从工厂功能生锈关闭(更多的,让我觉得我有什么应该工作...)

这是add_probe签名:https://sdroege.github.io/rustdoc/gstreamer/gstreamer/trait.PadExtManual.html#tymethod.add_probe

这是上面的错误(冒犯add_probe呼叫未注释):

error[E0277]: the trait bound `for<'r, 's, 't0> std::ops::Fn(&'r gst::Pad, &'s mut gst::PadProbeInfo<'t0>) -> gst::PadProbeReturn + std::marker::Send + std::marker::Sync: std::marker::Sized` is not satisfied
  --> src/main.rs:63:14
   |
63 |             .add_probe(gst::PadProbeType::BLOCK, *boxed_probe_fn);
   |              ^^^^^^^^^ `for<'r, 's, 't0> std::ops::Fn(&'r gst::Pad, &'s mut gst::PadProbeInfo<'t0>) -> gst::PadProbeReturn + std::marker::Send + std::marker::Sync` does not have a constant size known at compile-time
   |
   = help: the trait `std::marker::Sized` is not implemented for `for<'r, 's, 't0> std::ops::Fn(&'r gst::Pad, &'s mut gst::PadProbeInfo<'t0>) -> gst::PadProbeReturn + std::marker::Send + std::marker::Sync`

所以,我想由于在编译时不知道闭包的大小,我不能将其作为参数传递?

将解引用更改为位于“”上方的赋值行上。add_probe'给出了类似的错误:

error[E0277]: the trait bound `for<'r, 's, 't0> std::ops::Fn(&'r gst::Pad, &'s mut gst::PadProbeInfo<'t0>) -> gst::PadProbeReturn + std::marker::Send + std::marker::Sync: std::marker::Sized` is not satisfied
  --> src/main.rs:57:13
   |
57 |         let boxed_probe_fn = *create_boxed_probe_fn(x);
   |             ^^^^^^^^^^^^^^ `for<'r, 's, 't0> std::ops::Fn(&'r gst::Pad, &'s mut gst::PadProbeInfo<'t0>) -> gst::PadProbeReturn + std::marker::Send + std::marker::Sync` does not have a constant size known at compile-time
   |
   = help: the trait `std::marker::Sized` is not implemented for `for<'r, 's, 't0> std::ops::Fn(&'r gst::Pad, &'s mut gst::PadProbeInfo<'t0>) -> gst::PadProbeReturn + std::marker::Send + std::marker::Sync`
   = note: all local variables must have a statically known size

error[E0277]: the trait bound `for<'r, 's, 't0> std::ops::Fn(&'r gst::Pad, &'s mut gst::PadProbeInfo<'t0>) -> gst::PadProbeReturn + std::marker::Send + std::marker::Sync: std::marker::Sized` is not satisfied
  --> src/main.rs:63:14
   |
63 |             .add_probe(gst::PadProbeType::BLOCK, boxed_probe_fn);
   |              ^^^^^^^^^ `for<'r, 's, 't0> std::ops::Fn(&'r gst::Pad, &'s mut gst::PadProbeInfo<'t0>) -> gst::PadProbeReturn + std::marker::Send + std::marker::Sync` does not have a constant size known at compile-time
   |
   = help: the trait `std::marker::Sized` is not implemented for `for<'r, 's, 't0> std::ops::Fn(&'r gst::Pad, &'s mut gst::PadProbeInfo<'t0>) -> gst::PadProbeReturn + std::marker::Send + std::marker::Sync`

我理解基于堆栈的绑定需要编译时大小......所以这几乎感觉不可能做到,除非add_probe函数本身采用了盒装

更多的尝试。有几个地方,包括add_probe函数签名本身,使用了类型参数和“where”子句来指定Fn特征。

add_probe声明:https://github . com/SDR oege/gstreamer-RS/blob/db 3 Fe 694154 c 697 af daf 3 efb 6 EC 65332546942 E0/gstreamer/src/pad . RS

对于类型Fn,未实现使用“where”子句:Sized的后期重新编译

所以,让我们尝试一下,将create_boxed_probe_fn更改为:

fn create_boxed_probe_fn<F>(x: i32) -> Box<F>
    where F: Fn(&gst::Pad, &mut gst::PadProbeInfo) -> gst::PadProbeReturn + Send + Sync + 'static {
    Box::new(move |_, _| {
        println!("Idle... {}", x);

        gst::PadProbeReturn::Pass
    })
}

错误:

error[E0308]: mismatched types
  --> src/main.rs:15:18
   |
15 |           Box::new(move |_, _| {
   |  __________________^
16 | |             println!("Idle... {}", x);
17 | |
18 | |             gst::PadProbeReturn::Pass
19 | |         })
   | |_________^ expected type parameter, found closure
   |
   = note: expected type `F`
              found type `[closure@src/main.rs:15:18: 19:10 x:_]`

这似乎是因为我们已经指定了上面的类型,但闭包当然是它自己的类型。尝试以下不起作用,因为它是一个特征,并且不能使用“as”强制转换:

fn create_boxed_probe_fn<F>(x: i32) -> Box<F>
    where F: Fn(&gst::Pad, &mut gst::PadProbeInfo) -> gst::PadProbeReturn + Send + Sync + 'static {
    Box::new(move |_, _| {
        println!("Idle... {}", x);

        gst::PadProbeReturn::Pass
    } as F)
}

错误:

error[E0308]: mismatched types
  --> src/main.rs:15:18
   |
15 |           Box::new(move |_, _| {
   |  __________________^
16 | |             println!("Idle... {}", x);
17 | |
18 | |             gst::PadProbeReturn::Pass
19 | |         } as F)
   | |______________^ expected type parameter, found closure
   |
   = note: expected type `F`
              found type `[closure@src/main.rs:15:18: 19:15 x:_]`

error[E0605]: non-primitive cast: `gst::PadProbeReturn` as `F`
  --> src/main.rs:15:30
   |
15 |           Box::new(move |_, _| {
   |  ______________________________^
16 | |             println!("Idle... {}", x);
17 | |
18 | |             gst::PadProbeReturn::Pass
19 | |         } as F)
   | |______________^
   |
   = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait

它提到了“来自”特征。我没有研究过,因为暗示结束的特征似乎不对。我甚至不确定这是否可能?

我还尝试了他们似乎称之为类型归因的东西(而不是使用“:F”作为F“),但目前似乎不支持:https://github.com/rust-lang/rust/issues/23416

这个人也有同样的问题,但是他的解决方案似乎是不使用类型参数,而是指定Fn部分,而不使用where子句。(这是我最顶级的非工作尝试。)不完全确定,因为他没有发布他是如何修复它的。https://github.com/rust-lang/rust/issues/51154

给任何一个box版本添加impl关键字似乎都无济于事。像我在未装箱的“工作”版本中那样使用它的语法似乎是新的,我还没有找到关于它的很好的文档。以下是一些相关信息:https://github . com/rust-lang/rfcs/blob/master/text/1522-conservative-impl-trait . MD

更多相关链接:

如何将闭合件存储在Rust中?

Rust函数的返回类型中的闭包

预期的trait core::ops::FnMut,找到类型参数

https://doc.rust-lang.org/std/boxed/trait.FnBox.html

共有1个答案

牟正真
2023-03-14

<代码>框

        .add_probe(gst::PadProbeType::BLOCK, boxed_probe_fn);

这个问题可以简化为一个令人惊讶的简洁示例:

fn call<F: Fn()>(f: F) {
    f();
}

fn main() {
    let g = || ();                            // closure that takes nothing and does nothing
    let h = Box::new(|| ()) as Box<dyn Fn()>; // that but as a Fn() trait object
    call(g); // works
    call(h); // fails
}

问题的核心是

  1. 无法在trait对象上按值调用的方法。这使得无法调用Box

有可能为<code>框实现<code>Fn()

正如有人在问题评论中建议的,您可以创建自己的包装器结构来包装闭包,并实现< code>Box的< code>Fn()

您可以创建自己的trait<code>ProbeFn</code>,这是为任何正确类型的闭包实现的,并为<code>框实现<code>Fn()

在某些情况下,您可以使用

    call(&*h);

不同于<代码>框

 类似资料:
  • 问题内容: 我有一个javascript函数,该函数调用通用函数对服务器进行ajax调用。我需要从ajax调用的回调函数中检索结果(true / false),但是我得到的结果始终是’undefined’。 如果没有我的全部逻辑,泛型函数的超级简化版本将是: 调用它的函数将类似于: “结果”变量始终为“未定义”,并且对其进行调试,我可以看到正在执行回调函数的“返回真”行。 为什么会这样?如何将返回

  • 问题内容: 例如我有一个功能: 我怎样才能返回AJAX后得到的? 问题答案: 因为请求是异步的,所以您无法返回ajax请求的结果(而同步ajax请求是一个 糟糕的 主意)。 最好的选择是将自己的回调传递给f1 然后,您将像这样致电:

  • 本文向大家介绍C# 装箱和拆箱的知识回顾,包括了C# 装箱和拆箱的知识回顾的使用技巧和注意事项,需要的朋友参考一下 装箱是将值类型转换为 object 类型或由此值类型实现的任何接口类型的一个过程。 当 CLR 对值类型进行装箱时,会将该值包装到 System.Object 内部,再将后者存储在托管堆上。 拆箱将从对象中提取值类型。 装箱是隐式的;拆箱是显式的。 装箱和拆箱的概念是类型系统 C#

  • 问题内容: 我正在使用Postgresql 8.3,并具有以下简单功能,该功能会将a返回 给客户端 现在,我可以使用以下SQL命令来调用此函数并操纵返回的游标,但是游标名称是由PostgreSQL自动生成的 此外,如38.7.3.5中所述,显式地将游标名称声明为函数的输入参数 。返回游标。我可以声明自己的游标名称并使用此游标名称来操纵返回的游标,而不是为我自动生成的Postgresql吗?如果不是

  • 在程序 function_return.go 中我们将会看到函数 Add2 和 Adder 均会返回签名为 func(b int) int 的函数: func Add2() (func(b int) int) func Adder(a int) (func(b int) int) 函数 Add2 不接受任何参数,但函数 Adder 接受一个 int 类型的整数作为参数。 我们也可以将 Adder

  • 闭包作为参数(Taking closures as arguments) 现在我们知道了闭包是 trait,我们已经知道了如何接受和返回闭包;就像任何其它的 trait! 这也意味着我们也可以选择静态或动态分发。首先,让我们写一个获取可调用结构的函数,调用它,然后返回结果: fn call_with_one<F>(some_closure: F) -> i32 where F : Fn(i