docs
OpenGL 说明

OpenGL 说明

构建

movit 模块需要 movit 库,而该库通常不包含在发行版中,因为它(截至 2013 年 2 月)还非常新。使用 git clone http://git.sesse.net/movit/ 获取 movit 源代码。它可能会提示你需要 gtest、libpng 和 SDL 之类的东西,但就我们的需求而言 并不需要。我们只需要 Eigen3 和 GLEW,它们非常常见。然后,你可以运行 CXXFLAGS-fPIC ./autogen.sh, 并按需附加其他参数,例如也许可以加 --prefix。 把 -fPIC 放进 CXXFLAGS 很重要,因为我们要把这个库链接进一个共享库, 作为 MLT 插件。最后,运行 make libmovit.a install 只构建库本身,而不构建单元测试和演示程序。install 很重要,因为它会添加一个 pkg-config 文件, 并把一些运行时着色器(shader)程序放到 $prefix/share(datadir)中。到此为止,MLT 的 configure 应该就能检测到 libmovit。

面向 Melt 用户与脚本编写者

  • 所有 OpenGL 滤镜(filter)都在任何基于 CPU 的滤镜之前应用。
  • OpenGL 滤镜不接受 in 和 out 点。因此,它们作用于整个片段或整个轨, 具体取决于它被附加(attach)到哪儿(在 melt 中使用 -attach-clip 把滤镜附加到片段上,因为 -filter 是附加到轨上)。
  • 目前 OpenGL 滤镜都带有 glsl. 或 movit. 前缀。
  • 这些效果并不保证都有基于 CPU 的对应变体。
  • 使用 melt 命令行工具时,你需要使用 qglsl 消费器(consumer)来渲染。qglsl 消费器的行为 类似于 multi 消费器,但目前并不一定支持每种消费器组合—— 目前只用于驱动 avformat 消费器。
  • 如果 melt 和 xml 生产器(producer)在命令行或 XML 中看到以 movit. 或 glsl. 开头的 显式滤镜或转场(transition),它们会自动使用 qglsl;但你也可以通过在命令行任意位置添加属性 glsl.=1 来强制启用。你也可以在 XML 生产器上强制启用:在 XML 文件名后添加 qglsl=1 查询字符串参数。当然,查询字符串参数会破坏按文件名扩展名识别类型的处理, 你必须通过在文件名前加 “xml:” 显式请求 XML 生产器。例如:melt xml:myfile.mlt?qglsl=1
  • 你应避免在 GPU 滤镜与 GPU 转场之间添加任何 CPU 滤镜, 不过这可能难以完全避免。
  • 环境变量 MLT_MOVIT_PATH 告诉 MLT Movit 着色器程序文件的位置, 以防你在 movit 和 MLT 最近一次构建之后没有安装或移动这些文件。
  • 目前不要尝试在消费器属性 real_time > 1 或 < 1 时使用并行渲染线程。
  • xgl 消费器只能在带 X11 的系统上工作,不提供声音, 通常只对使用 melt 进行排障和原型验证有用。你也可以使用 -consumer qglsl 0=sdl2,但已知它在退出时会崩溃。
  • 如果你使用 WebVfx,那么 qglsl 与 qmelt(基于简单 Qt 应用的 melt 封装程序)不兼容。 此外,movit 模块通常与 webvfx 不兼容。

服务模块

  • movit.blur
  • movit.convert(色彩空间转换)
  • movit.crop
  • movit.diffusion
  • movit.glow
  • movit.lift_gamma_gain(色彩校正)
  • movit.mirror
  • movit.opacity
  • movit.rect(位置与缩放)
  • movit.resample
  • movit.resize(填充)
  • movit.saturation
  • movit.sharpen
  • movit.vignette
  • movit.white_balance
  • movit.mix(转场)
  • movit.overlay(转场)
  • qglsl 消费器(multi 消费器的一层封装,使用 Qt 抽象平台相关的 OpenGL 上下文)
  • xgl 消费器(仅 X11 视频)
  • glsl.manager(仅供应用使用的特殊滤镜)

movit.convert、movit.crop、movit.resample 和 movit.resize 并非设计为直接使用。 它们是自动添加的规范化滤镜。使用普通的 crop 滤镜把裁剪参数传给 movit.crop。使用新的 movit.rect 滤镜来影响 movit.resample 和 movit.resize,以便对缩放和填充有更多控制。这与 movit.overlay 配合使用, 可以实现类似传统基于 CPU 的 composite 或 affine 服务模块的效果。 换言之,geometry 参数存放在一个独立的 movit.rect 滤镜上,而 movit.overlay 只负责合成。目前没有任何东西能做旋转或斜切。

使用 melt -query 可以获得关于这些服务模块的更多信息, 包括属性。

面向应用开发者

你将希望在应用中使用 OpenGL 作为显示机制,以获得此事的最大收益。 这意味着你需要为你的显示创建一个 OpenGL 上下文,并再创建一个与之“共享”的上下文, 作为 MLT 的“渲染上下文”。

你必须分配一个 “glsl.manager” 滤镜,首先确定 MLT 的构建是否包含 movit 模块。 接下来,你需要在该滤镜上触发 MLT 事件 “init glsl”,让 MLT 检测 GL 扩展。然而重要的是, 你必须在渲染上下文有效且在实际进行渲染的线程上处于当前(current)状态时 触发该事件!因此,有一个新的 MLT 消费器事件 “consumer-thread-started”,你必须处理它。你应该在这个处理函数中把渲染上下文设为当前, 并在 glsl.manager 上触发 “init glsl”。参见 src/modules/qimage/consumer_qglsl.cpp 的一个简单示例。

触发 “init glsl” 之后,你可以获取以整数表示布尔值的属性 “glsl_supported”,以确定本机的 OpenGL 子系统是否满足要求。最后, 你应该有一个消费器 “consumer-frame-show” 事件处理函数,对传入的 frame 参数调用 mlt_frame_get_image() 或 Mlt::Frame::get_image()。你可以传入任意 mlt_image_format,但如果你想集成由 MLT 渲染的 GL 纹理,就使用 mlt_image_opengl_texture。返回的图像指针必须被转换为 GLuint 指针, 其中包含纹理名称。如果你使用 real_time = 1 或 -1,那么应用的显示 GL 上下文 可以是当前的;但如果你使用 real_time = 0,则渲染上下文必须是当前的。

正如 “init glsl” 事件必须在渲染线程上的 glsl.manager 滤镜上触发一样, 你也应在销毁 OpenGL 上下文之前,在渲染线程上触发 “close glsl”。 这会使它释放其创建的所有帧缓冲、像素缓冲和纹理对象。 通常你在一个 “consumer-thread-stopped” MLT 事件监听器中完成这件事,以便它在渲染线程上执行。 如果你的应用会停止并重启消费器,触发该事件就尤为重要。如果应用停止后就直接关闭, 那么就没有必要,因为 glsl.manager 的析构函数会负责清理。

此外,qglsl 消费器注册了一个 MLT 事件 “consumer-cleanup”,你可以在关闭生产器之前触发它。 是这样的:当生产器被关闭时,可能会进入 movit 的析构函数,而这些析构函数会调用 GL 函数来释放 GL 资源。这些释放操作应当在分配它们的那个上下文——即渲染上下文——上执行。 然而,这种关闭常常发生在另一个线程上,比如应用的主事件线程。因此,qglsl 消费器会在你 即将关闭生产器的那个线程上把渲染上下文设为当前。

覆盖渲染线程管理

自 2014 年 1 月 1 日起,消费器上新增了两个事件,用于覆盖线程的创建与清理。 基本上,它们让你可以使用另一种线程实现(例如 Qt QThread)来管理线程。 如果你需要在 Qt 主线程上创建上下文,但又需要把上下文移到另一个线程,这会很有用—— 而移上下文只对 QThreads 有效,对 pthreads 无效。

第一个是 “consumer-thread-create”,用于创建并启动一个线程。除了标准的 前两个 MLT 事件处理参数之外,它还提供了以下额外参数: void** handle, int* priority, thread_function_t, void* data

handle 是一个返回变量,即线程上下文对象指针,等价于 pthread_t 或 QThread。

priority 是 POSIX 的 sched_priority

thread_function_t 是一个函数指针,签名如下: void* f(void *data)

data 是传给该函数指针的不透明指针。

你的新线程必须运行 function(data)。

第二个事件是消费器对象上的 “consumer-thread-join”。事件处理函数应等待线程完成 (pthread_join),然后清理线程资源,包括析构 handle。除了标准的前两个 MLT 事件处理参数之外,该事件还提供一个额外参数: void* handle

同步

自 2014 年 1 月 12 日起,movit 模块不再调用 glFinish(), 因为它会带来不良的副作用。如果你使用 mlt_image_opengl_texture 渲染到 GL 纹理, 并且在另一个线程中使用这些纹理——例如用于显示——那么你需要基于 GL fence sync object (opens in a new tab) 进行同步。 基本上,在你调用 mlt_frame_get_image() 或 Mlt::Frame::get_image() 之后、 尝试用 GL 函数使用纹理 ID 之前,你需要从 frame 的 “movit.convert.fence” 数据属性中 获取 sync 对象指针,并在其上调用 glClientWaitSync() (opens in a new tab):

GLsync sync = (GLsync) frame.get_data("movit.convert.fence");
if (sync)
    glClientWaitSync(sync, 0, GL_TIMEOUT_IGNORED);