框架设计

  • 前言
    • 目标群众
  • 基本概述
    • 基础设计信息
    • 结构与流程
    • 用法
      • Hello World
      • 工厂
      • 服务属性
      • 播放列表
      • 滤镜
      • 附加滤镜
      • 介绍混合(混合过渡)
      • 多轨道与过渡

前言

MLT是为电视广播设计的开源多媒体框架。严格来说,它为使项目包含新的音视频源、 滤镜、场景过渡和播放设备提供了可插拔式的架构。

本框架为使用了MLT的服务或应用程序提供了结构体系与实用功能。

就框架本身而言,它只提供了为管理资源,如内存,属性,动态对象加载和实例化服务的抽象类和实用功能程序。

本文档大致分为三部分。第一部分提供对MLT的基础描述,第二部分展示了它如何被使用,最后一部分则结合了扩展系统的强调提示展示了框架的结构与设计。

目标群众

本文档作为一份框架的”产品路线图“被提供,并且对那些想在框架层次进行开发的人来说,请务必考虑通读本文。

这包括:
 1. 框架维护人员
 2. 模块开发者
 3. 应用开发者
 4. 任何对MLT感兴趣的人

本文档强调的部分在于公共接口,而非具体实现细节。

阅读 MLT 客户端/服务端集成是没有必要的,请参考libmvsp.txt和mvsp.txt寻找关于此区域的更多信息。

基本概述

基础设计信息

MLT使用C语言编写,框架除了C99标准与pthread库外没有别的依赖。它遵循基本的面向对象设计范式,许多设计宽松地基于生产者/消费者设计模式。

它为音频与视频特效应用了逆波兰表达。

框架被设计为不影响色彩空间——然而当前实现的模块非常趋向于8bit YUV422格式,但是理论上,这些模块可以被完全替换掉。

一些关于这些术语的粗略解读将贯穿于这篇文档的剩余部分。

结构与流程

一个MLT ‘网络’ 的总体结构可描述为一个‘生产者’与一个‘消费者’之间的连接:

一个典型的消费者从生产者处请求MLT帧对象,随后对其进行一些操作,并在完成一帧后将其关闭。

一个常见的对此处使用到的”生产者/消费者“术语的混肴是,消费者也许会’生产‘某些东西。举个例子,libdv消费者生产DV并且libdv生产者似乎会去消耗DV。然而,此处的命名约定仅表示MLT 帧对象的生产者与消费者。

换言之——一个生产者生产MLT帧对象,并且一个消费者将消耗MLT帧对象。

一个MLT帧本质上提供一张未压缩的,且与音频样本相关联的图片。

滤镜也可以被放置于生产者与消费者之间:

一个’服务’是一组生产者、滤镜、消费者的集合名称。

连接起来的生产者与消费者或服务之间的交流将执行三个阶段:

  • 获取帧
  • 获取图像
  • 获取音频

MLT采用‘惰性评估’——图像与音频不需要从源中解压,直到获取图片与音频的方法被引用。

实质上,消费者从其所连接的模块中获取资源——这意味着线程通常属于消费者实现的领域,并且在消费者类上提供了一些基本方法以确保实时吞吐量。

用法

Hello World

在我们进入框架架构细节之前,下面提供了一个有效的使用例。

以下部分简单的提供了一个媒体播放器:

#include <stdio.h>
#include <unistd.h>
#include <framework/mlt.h>int main( int argc, char *argv[] )
{// Initialise the factoryif ( mlt_factory_init( NULL ) == 0 ){// Create the default consumermlt_consumer hello = mlt_factory_consumer( NULL, NULL );// Create via the default producermlt_producer world = mlt_factory_producer( NULL, argv[ 1 ] );// Connect the producer to the consumermlt_consumer_connect( hello, mlt_producer_service( world ) );// Start the consumermlt_consumer_start( hello );// Wait for the consumer to terminatewhile( !mlt_consumer_is_stopped( hello ) )sleep( 1 );// Close the consumermlt_consumer_close( hello );// Close the producermlt_producer_close( world );// Close the factorymlt_factory_close( );}else{// Report an error during initialisationfprintf( stderr, "Unable to locate factory modules\n" );}// End of programreturn 0;
}

这是一个简单的例子——它不提供任何查找功能或运行时配置设定。

任何MLT应用程序的第一步都是工厂的初始化——这保证了环境配置与MLT能够正常运行。下面是对工厂的细节介绍。

如上例的mlt_factory_consumer与mlt_factory_producer调用,所有的服务都通过工厂实例化。对于滤镜和过渡效果也有类似的工厂。在services.txt中包括了标准服务的详细信息。

此处要求的默认值是一个特殊情况——NULL使用请求代表使用默认的生产者与消费者。

默认生产者是“加载器(loader)”。此生产者通过匹配文件名来定位要使用的服务,并且附加‘标准化滤镜’(如缩放、去交错、重采样和字段标准化器) 到加载的内容当中——这些滤镜保证了消费者得到它所寻求的结果。(Frame?)

默认的消费者是“sdl”。加载器与sdl的组合将提供一个媒体播放器。

在这个例子当中,我们连接生产者并随后启动消费者。我们接下来等待直到消费者运行停止 (此例子中指关闭SDL_Window这一动作) 并在应用程序退出前最终关闭消费者、生产者与工厂。

注意,消费者是线程化(异步?)的——在启动消费者之后以及停止或关闭消费者之前,总是需要等待一些种类的事件。

另外也请注意。你可以重载默认值如下:
MLT_CONSUMER=xml ./hello file.avi
这将在标准输出上创建一个xml文档
MLT_CONSUMER=xml MLT_PRODUCER=avformat ./hello file.avi
这将会直接使用avformat生产者播放视频,因此他将避开标准化方法。
MLT_CONSUMER=libdv ./hello file.avi > /dev/dv1394
如果您足够幸运,可以随手将file.avi实时转换为DV格式,并将其广播到您的DV设备中。

工厂

正如’Hello World’例子中所展示的那样,工厂可以创建服务对象。

框架本身不提供服务——服务以插件的形式被提供。插件以"模块"的形式组织,并且一个模块可以提供许多不同种类的服务。

一旦工厂被初始化,所有配置好的服务就都可以被使用。

mlt_factory_prefix()返回安装各模块的目录路径,这可以被明确表示在mlt_factory_init调用其本身种,或它可以通过环境变量MLT_REPOSITORY明确表示,亦或者在这两者都缺乏配置的情况下,它将默认返回安装了prefix/shared/mlt/modules的路径。

mlt_environment()提供与如下表格中所示的名称=值集合的只读连接:

名称 描述
MLT_NORMALISATION 系统的标准化 PAL或NTSC
MLT_PRODUCER 默认生产者 “loader”或其他
MLT_CONSUMER 默认消费者 "sdl"或其他
MLT_TEST_CARD 默认检测卡生产者 任何生产者

这些值将从同名的环境变量中初始化。

如上方所展示的,一个生产者可以用"默认标准化"生产者来创建,并且他们也可以被使用名字来请求。滤镜和过渡总是被用名字来请求——此处没有它们的’默认’概念。

服务属性

所有的服务都拥有它们能够用来操纵影响它们行为的属性集合。

为了在服务上设置属性,我们需要检索与他相联系的属性。对生产者来说,这是被调用以下方法完成的:

mlt_properties properties = mlt_producer_properties(producer);

所有的服务都有一个相似的关系方法。
一旦完成了数据检索,设置与获取属性就能够直接在这个对象上操作完成,举个例子:

mlt_properties_set(properties, "name", "value");

更多关于属性对象的完备的描述可以在下方找到。

播放列表

到目前为止,我们已经展示了一个简单的生产者/消费者配置——下一阶段是要将生产者组织到播放列表当中。

假设我们正在改写"Hello World"样例,并且希望添加一系列文件到播放队列,即:

hello*.avi

我们将创建一个新的名为create_playlist的函数而不是直接调用mlt_factory_producer。此函数负责创建播放列表,创建每一个生产者并将它们添加到播放列表当中。

mlt_producer create_playlist( int argc, char **argv )
{// We're creating a playlist heremlt_playlist playlist = mlt_playlist_init( );// We need the playlist properties to ensure clean upmlt_properties properties = mlt_playlist_properties( playlist );// Loop through each of the argumentsint i = 0;for ( i = 1; i < argc; i ++ ){// Create the producermlt_producer producer = mlt_factory_producer( NULL, argv[ i ] );// Add it to the playlistmlt_playlist_append( playlist, producer );// Close the producer (see below)mlt_producer_close( producer );}// Return the playlist as a producerreturn mlt_playlist_producer( playlist );
}

注意到我们在添加生产者到播放列表后就关闭了它们,实际上,我们所做的是关闭我们对此生产者的引用——播放列表创建了属于播放列表自身的对生产者的引用并将其插入,并且当播放列表被销毁时,它会关闭自身对生产者的引用。

还要注意,如果添加同一生产者的多个实例到播放列表,它将创建对其的多个引用。

现在我们所要做的是替换主函数中的这一行:

// Create a normalised producermlt_producer world = mlt_factory_prodi

// Create a normalised producermlt_producer world = create_playlist(argc, argv);

然后我们便有方法去播放复数片段。

[*] 此处的引用设计在MLT 0.1.2中有介绍——它100%适用于早期的注册引用与销毁播放列表对象的属性。

滤镜

在生产者和消费者之间插入滤镜只是对其的一种实例化,第一步滤镜连接到生产者,再将滤镜连接到消费者。

举个例子:

// Create a producer from something
mlt_producer producer = mlt_factory_producer( ... );// Create a consumer from something
mlt_consumer consumer = mlt_factory_consumer( ... );// Create a greyscale filter
mlt_filter filter = mlt_factory_filter( "greyscale", NULL );// Connect the filter to the producer
mlt_filter_connect( filter, mlt_producer_service( producer ), 0 );// Connect the consumer to filter
mlt_consumer_connect( consumer, mlt_filter_service( filter ) );

与生产者和消费者一样,滤镜也能被通过修改它的属性对象操作——mlt_filter_properties方法能够被调用并且属性可以按要求进行设定。

滤镜连接函数中的附加参数很重要,因为它规定了滤镜运作的’轨迹’。对基础的生产者与播放列表,它们只有一个轨迹(0),正如您将在下一节看到的,即使有多个轨迹也只有单一的能够产生输出。

附加滤镜

所有的服务都可以拥有附加滤镜。

考虑下面的例子:

// Create a producermlt_producer producer = mlt_factory_producer( NULL, clip );// Get the service object of the producermlt_producer service = mlt_producer_service( producer );// Create a filtermlt_filter filter = mlt_factory_filter( "greyscale" );// Create a playlistmlt_playlist playlist = mlt_playlist_init( );// Attach the filter to the producermlt_service_attach( producer, filter );// Construct a playlist with various cuts from the producermlt_playlist_append_io( producer, 0, 99 );mlt_playlist_append_io( producer, 450, 499 );mlt_playlist_append_io( producer, 200, 399 );// We can close the producer and filter nowmlt_producer_close( producer );mlt_filter_close( filter );

当播放结束时,灰度缩放滤镜将会对播放列表中来自特点生产者的每一帧执行处理。

此外,每个剪辑都可以拥有它们自身的附加滤镜,这些滤镜将在生产者的滤镜后执行。举个例子:

// Create a new filter
filter = mlt_factory_filter( "invert", NULL );// Get the second 'clip' in the playlist
producer = mlt_playlist_get_clip( 1 );// Get the service object of the clip
service = mlt_producer_service( producer );// Attach the filter
mlt_service_attach( producer, filter );// Close the filter
mlt_filter_close( filter );

甚至播放列表本身也可以附加滤镜

// Create a new filter
filter = mlt_factory_filter( "watermark", "+Hello.txt" );// Get the service object of the playlist
service = mlt_playlist_service( playlist );// Attach the filter
mlt_service_attach( service, filter );// Close the filter
mlt_filter_close( filter );

当然,播放列表作为生产者,可以被切分并放置在另一个播放列表上,并且滤镜可以被添加到这些切分或新的播放列表本身上。

附加滤镜的主要优势是它们能够保持连接并且不会遭受插入项目 和计算替换出入点等维护问题的影响——如果您在多轨道领域内大量使用插入分离式的滤镜,这将会成为一个主要问题。

介绍混合(混合过渡)

混合是最简单的介绍播放列表中相邻的两个切片间过渡的方式
考虑存在下面的播放列表:

让我们假设’X’是由50帧长度的黑幕组成

当播放时,将会获得50帧黑幕,然后突然开始播放A,紧接着切换到播放B,最终又播放黑幕’X’

我们那的目的是将播放列表转换为像这样的模式:

当一块切片中指向两个切片时会展示一个过渡,注意这个播放列表会较上一个短一些,这也是我们所期望的:再两个切片中添加一个简单的50帧过渡来代替直接播放50帧黑幕。

这一工作使用mlt_playlist_mix方法来完成,因此,假设你有一个像原图中那样的播放列表,在做第一个混合片段时,你可以这样做:

// 创建一个过渡
mlt_transition transition = mlt_factor_transition( "luma", NULL );// 将第一个和第二个切片混合50帧长度
mlt_playlist_mix( playlist, 0, 50, transition );// 关闭过渡
mlt_transition_close( transition );

这将会提供给你第一个过渡(转场)注意这将会在播放列表中创建一个新的切片。

作为一般提示,为了获取下一个切片索引的这一需求,你需要仿照以下的例子:

// 获得播放列表中的切片数量
int i = mlt_playlist_count( );// 通过逆序迭代它们
while ( i -- )
{// 开启转场mlt_transition transition = mlt_factor_transition( "luma", NULL );// 混合第1,2个切片的各50帧mlt_playlist_mix( playlist, i, 50, transition );// 关闭转场mlt_transition_close( transition );
}

这是另一种像在新创建的切片和当前切片间使用mlt_playlist_join的方式(你可以通过比较调用mix命令前后播放列表的长度变化来确定新的切片是否被创建)

内部的,mlt_playlist_mix调用生成了像下方所描述那样的多轨道。如同附加滤镜,混合操作使插入切片到列表中变得非常简单。

同样需要注意:混合使用允许一个简单的用户接口 - 不同于强制使用复杂的多轨道对象,你可以在单个轨道上进行许多操作。因此,额外的轨道可以提供给音频混录、复合等,混合或合成等被独立定位的操作而不会影响到其他轨道。

多轨道与过渡

MLT框架处理多轨道时需要受到两个约束:

  1. 消费者和生产者需要通过一个框架相互通信
  2. 能够序列化和操作一个‘网络’(或者说滤镜图)

我们可以可视化一个多轨道模型如下:

轨道0和轨道1的重合区域可能会又某种转场 - 没有转场时,从b1到a2中的帧将在重叠区域中显示(由高到低图层叠加顺序)

MLT有一个多轨道对象,但它并不是生产者,因为它可以直接连接到使用者,并且消费者会像对待普通生产者一样对待它。在上图的多轨道模型种,除了剪辑之间的过渡外,从轨道1种看不到任何a1-a2相关的东西

消费者从与其连接的生产者处拉出一帧,而多轨道模型种的每个轨道提供一帧,必须在某处确保能够拉出每一条轨道中的帧,并选择正确的方式进行传递。

因此,MLT为多轨道模型提供了封装(称为‘tractor’轨道头、牵引头)牵引头会保证输出正确的帧以及保证所具有的类似生产者行为。


有了牵引头和多轨道模型的连接,现在我们可以将多轨道连接到消费者。

轨道可以是生产者,播放列表,甚至其他的牵引头。

现在我们希望在牵引头和多轨道模型种插入滤镜和转场,我们可以直接使用插入滤镜在二者中间完成这一工作,但是这涉及到左右生产者和消费者的连接和重连,似乎只有我们令这一进程自动化后才显得很恰当。

新的概念‘field’场地诞生了,我们在场地种‘种植’滤镜和转场,牵引头拉动多轨道模型经过场地并生产出一帧。

这样,我们需要先创建牵引头,然后跟上我们获得的多轨道模型和场地对象。我们能够为他们输入数据并最终将牵引头连接到一个消费者

本质上来说,它在消费者看起来是这样的:

一个例子将帮助我们如愿以偿地弄清楚它。

让我们假设要提供‘水印’到我们的‘hello world’例子当中,我们已经扩展了例子到能够播放多个切片,现在我们将放置一个基于文本的水印,于左上角写着‘Hello World’:

mlt_producer create_tracks( int argc, char **argv )
{// 创建牵引头mlt_tractor tractor = mlt_tractor_new( );// 创建场地对象mlt_field field = mlt_tractor_field( tractor );// 创建多轨道对象mlt_multitrack multitrack = mlt_tractor_multitrack( tractor );// 创建一个复合转场mlt_transition transition = mlt_factory_transition( "composite", "10%/10%:15%x15%" );// 创建轨道0mlt_producer track0 = create_playlist( argc, argv );// 创建水印轨道1mlt_producer track1 = mlt_factory_producer( "loader", "pango" );// 获得轨道0的长度mlt_position length = mlt_producer_get_playtime( track0 );// 设置轨道1的属性mlt_properties properties = mlt_producer_properties( track1 );mlt_properties_set( properties, "text", "Hello\nWorld" );mlt_properties_set_position( properties, "in", 0 );mlt_properties_set_position( properties, "out", length - 1 );mlt_properties_set_position( properties, "length", length );mlt_properties_set_int( properties, "a_track", 0 );mlt_properties_set_int( properties, "b_track", 1 );// 现在设置转场的属性properties = mlt_transition_properties( transition );mlt_properties_set_position( properties, "in", 0 );mlt_properties_set_position( properties, "out", length - 1 );// 添加我们的轨道到多轨道模型种mlt_multitrack_connect( multitrack, track0, 0 );mlt_multitrack_connect( multitrack, track1, 1 );// 种植滤镜到场地mlt_field_plant_transition( field, transition, 0, 1 );// 关闭引用mlt_producer_close( track0 );mlt_producer_close( track1 );mlt_transition_close( transition );// 返回牵引头return mlt_tractor_producer( tractor );
}

现在我们所要做的就是替换主函数中的这些行

// 创建播放列表
mlt_producer world = create_playlist(argc, argv);

用以下内容替换:

// 创建带水印的播放列表
mlt_producer world = create_tracks(argc, argv);

我们有方法去播放多个带巨大可怕水印切片 - 正如我们的样例world需要的那样,对吧?

顺便一提,在生产者与消费者之间插入更简单的水印滤镜也可以实现相同的目的。

MLT 框架设计文档翻译相关推荐

  1. MegEngine 框架设计

    MegEngine 框架设计 MegEngine 技术负责人许欣然将带了解一个深度学习框架是如何把网络的定义逐步优化并最终执行的,从框架开发者的视角来看待深度学习. 背景 AI 浪潮一波又一波,仿佛不 ...

  2. 软件框架设计的艺术----读书总结

    总结 软件开发的艺术 理想主义,经验主义和无绪 文艺复兴时期,现代科学产生了两个重量级理论: 理性主义和经验主义. 理性主义认为理智是信息的首要来源.给出一个假设,只要通过思考就能理解和描述这个世界, ...

  3. 从 Servlet 入手带你看架构和框架设计的套路

    以下代码相信大家都很熟悉,大学时学 Java Web 都写过这样的代码. 从第一次接触 Servlet 到之后的很长一段时间内,我都没理解 Servlet 是个什么玩意? 为什么要有 Servlet ...

  4. Java 反射:框架设计的灵魂

    作者 l 会点代码的大叔(CodeDaShu) 在学习 Java 反射之前,先让我们看看这几个概念. 01 解释型语言和编译型语言 解释型语言:不需要编译,在运行的时候逐行翻译解释:修改代码时可以直接 ...

  5. 《精通自动化测试框架设计》—第1章 1.3节五天太久,还能压缩吗

    本节书摘来自异步社区<精通自动化测试框架设计>一书中的第1章,第1.3节五天太久,还能压缩吗,作者陈冬严 , 邵杰明 , 王东刚 , 蒋涛,更多章节内容可以访问云栖社区"异步社区 ...

  6. 通用数据级别权限的框架设计与实现(4)-单条记录的权限控制

    查看上篇文章通用数据级别权限的框架设计与实现(3)-数据列表的权限过滤,我们开始在原来的基础上实现单条权记录的权限控制. 相信前面的列表权限控制,很多系统都可以做到,但如何在上面列表的权限过滤中实现通 ...

  7. 自动化测试基础篇--Selenium框架设计(POM)

    一.自动化测试框架 感谢木棉花的漂泊分享,内容转自链接:http://www.cnblogs.com/fengyiru6369/p/8053035.html 1.什么是自动化测试框架 简单来说,自动化 ...

  8. 《精通自动化测试框架设计》—第2章 2.3节测试数据交互基本方法

    本节书摘来自异步社区<精通自动化测试框架设计>一书中的第2章,第2.3节测试数据交互基本方法,作者陈冬严 , 邵杰明 , 王东刚 , 蒋涛,更多章节内容可以访问云栖社区"异步社区 ...

  9. 《精通自动化测试框架设计》目录—导读

    作者简介 精通自动化测试框架设计 陈冬严,浙江大学硕士,具有10年软件测试和团队管理的工作经验,先后服务于ITSM.PLM软件研发企业,现就职于某金融行业核心机构IT规划部门.业余时间喜欢园艺. 邵杰 ...

最新文章

  1. 2021全球开放数据应用创新大赛开启,300万+奖金等你来拿!
  2. Java Review - 并发编程_StampedLock锁探究
  3. 诸葛io的技术架构图_【总结】MySQL技术内幕二:InnoDB存储引擎技术特性
  4. 分布式精华问答 | 秒懂分布式与集群的区别
  5. magicmatch java_Java-webmagic爬虫
  6. SQLMAP参数中文解说
  7. 【clickhouse】clickhouse 主从配置 从节点无数据
  8. 处理接口超时_架构设计 | 接口幂等性原则,防重复提交Token管理
  9. brew更新的时候不更新某个应用_可以不可以第六集剧情介绍,日剧可以不可以什么时候更新...
  10. 银行卡四元素/四要素校验API,银行卡实名认证接口文档
  11. java计算器功能_java实现简易计算器功能
  12. 360开源的插件化框架Replugin深度剖析
  13. 7月1日天刀服务器维护,天涯明月刀7月1日满级新服_天刀满级新服天命风流入君怀_3DM网游...
  14. Java判断邮箱格式是否正确
  15. html实现手机截屏,iPhone手机如何实现网页长截图?
  16. 开机时HP Hotkey UWP Service占用内存过高
  17. AD9854的工作原理和应用电路图
  18. web实现微信9宫格
  19. 过支付宝反Xposed登录检测
  20. 人脸识别原理:(初级篇)内含PPT

热门文章

  1. 王凯丽的艺术人生,不忘初心,方的始终的真正含义
  2. 前端性能优化:dns-prefetch和preload预加载资源
  3. ftp、 pop3、http 、SMTP等协议介绍
  4. ajax怎么解决报414,如何解决HTTP 414“请求URI太长”错误?
  5. 腾讯!网易!那些混不下去的互联网公司
  6. 证券基础知识——一手代表多少数量?
  7. c++ 读取csv文件格式点云
  8. 胜天半子丨数字化供应链中台解决方案能给品牌商带来怎样的机遇变化?
  9. mysql如何加悲观锁_【mysql】关于悲观锁
  10. dig怎么读(digger怎么读)