Документация по инфраструктуре
Copyright (C) 2004 Ushodaya Enterprises Limited
Author: Charles Yates <charles.yates@pandora.be>
Last Revision: 2004-10-08
Translator: Ruslan Popov <radz@yandex.ru>
Last Revision: 2006-01-06
Инфраструктура MLT
Введение
Библиотека MLT является мультимедийной инфраструктурой разработанной для телевизионного вещания и, соответственно, она предоставляет собой расширяемую архитектуру для подключения новых A/V источников, фильтров, переходов и устройств воспроизведения.
Инфраструктура является основным каркасом на основе которого создаются приложения и сервисы использующие библиотеку MLT.
Сама инфраструктура предоставляет нечто большее, чем просто "абстрактные классы" и утилиты для управления ресурсами такими как память, свойства, динамическая подгрузка объектов и обслуживание самого экземпляра библиотеки.
Данный документ разделён на три секции. Первая секция предоставляет собой простой обзор библиотеки MLT, вторая секция показывает как можно использовать библиотеку и заключительная секция показывает дизайн и структуру библиотеки, ставя основной акцент на расширение возможностей.
Целевая Аудитория
Данный документ можно рассматривать как "дорожную карту" по инфраструктуре библиотеки, которая должна быть обязательно изучена тем, кто желает разрабатывать исходный код на уровне библиотеки MLT.
Перечислим таких людей:
- разработчики библиотеки;
- разработчики модулей;
- разработчики приложений;
- любой, кому интересна данная библиотека.
Цель данного документа объяснить использование интерфейсов, а не описывать детали реализации библиотеки.
Данный документ не обязателен для чтения в случае MLT клиент-сервер интеграции. Для этого надо обратиться к
valerie.txt и
dvcp.txt.
Секция 1 - Основы
Дизайн
Библиотека написана на языке C.
Библиотека соответствует стандарту C99 и зависит только от POSIX библиотек.
Библиотека следует основной парадигме объекто-ориентированного дизайна и, следовательно, в большинстве случаев использует шаблон "Производитель (Producer)/Потребитель (Consumer)".
Библиотека использует обратную польскую нотацию для приложений A/V эффектов.
Библиотека разработана так, чтобы быть нейтральной к используемому пространству цветов, но реализованные в настоящее время модули, тем не менее, в своём большинстве ориентированы на 8-ми битное YUV422 пространство цветов. В теории, эти модули могут быть в последствии заменены.
Неопределённость данного срока подразумевается по всему документу.
Структура и поток
Основной структурой "сети" библиотеки является простое соединение "Производителя" к "Потребителю":
+--------+ +--------+
|Producer|-->|Consumer|
+--------+ +--------+
Обычный потребитель запрашивает объекты
MLT Frame у производителя, производит с объектами необходимые действия и закрывает их.
Общий беспорядок с терминологией производителя/потребителя, используемой здесь, состоит в том, что потребитель может 'произвести' кое-что. Например,
libdv потребитель производит
DV поток и кажется как-будто
libdv производитель потребляет этот поток. Однако, соглашения по обозначениям относятся только к производителям и потребителям
MLT Frame.
Другими словами, производитель производит объекты
MLT Frame, а потребитель их потребляет.
По существу,
MLT Frame предоставляет несжатое изображение и связанные с ним звуковой поток.
Между производителем и потребителем могут помещаться фильтры:
+--------+ +------+ +--------+
|Producer|-->|Filter|-->|Consumer|
+--------+ +------+ +--------+
Сервис это коллективное имя для производителей, фильтров, преобразований и потребителей.
Взаимодействие между подсоединёнными потребителем и производителем или сервисов выполняются в три шага:
- получение информации о кадре;
- получение изображения;
- получение звука.
Библиотека использует "ленивую распаковку", т.е. изображение и и связанный с ним звуковой поток не нуждаются в распаковке до первого вызова соответствующих методов.
В основном, потребитель получает информацию из того, к кому он подключен. Это означает, что многопоточность реализуется на стороне потребителя и с его стороны предоставляется некая базовая функциональность, которая будет гарантировать пропускную способность в реальном времени.
Секция 2 Использование
Hello World
Перед тем как погрузиться в архитектурные дебри библиотеки следует предоставить работающий пример.
Нижеприведённый код реализует медиа проигрыватель:
#include <stdio.h>
#include <unistd.h>
#include <framework/mlt.h>
int main( int argc, char *argv[] )
{
// Initialise the factory
if ( mlt_factory_init( NULL ) == 0 )
{
// Create the default consumer
mlt_consumer hello = mlt_factory_consumer( NULL, NULL );
// Create via the default producer
mlt_producer world = mlt_factory_producer( NULL, argv[ 1 ] );
// Connect the producer to the consumer
mlt_consumer_connect( hello, mlt_producer_service( world ) );
// Start the consumer
mlt_consumer_start( hello );
// Wait for the consumer to terminate
while( !mlt_consumer_is_stopped( hello ) )
sleep( 1 );
// Close the consumer
mlt_consumer_close( hello );
// Close the producer
mlt_producer_close( world );
// Close the factory
mlt_factory_close( );
}
else
{
// Report an error during initialisation
fprintf( stderr, "Unable to locate factory modules\n" );
}
// End of program
return 0;
}
Это простой пример. Он не предоставляет возможности свободного перемещения по контенту или изменения конфигурации.
Первым шагом любого
MLT приложения является инициализация фабрики, которая удостоверяет, что окружение настроено и библиотека может функционировать. Фабрика будет рассмотрена далее.
Все сервисы запускаются через фабрики, как это показано вызовами
mlt_factory_consumer и
mlt_factory_producer. Для фильтров и переходов существуют подобные фабрики. В
services.txt описаны все подробности обо всех стандартных сервисах библиотеки.
Использование значений
NULL является особым случаем. Таким образом мы запросили производителей и потребителей определённых по умолчанию.
Производителем по умолчанию является
fezzik. Этот производитель просматривает имена файлов, чтобы найти подходящий сервис и подключает "нормализованные фильтры" (такие как масштабирование, деинтерлейсинг, изменение частоты звука и нормализация полей кадра) к загруженному контенту. Эти фильтры гарантируют, что потребитель получет именно то, что запросил.
Потребителем по умолчанию является
sdl. Комбинация из
fezzik и
sdl предоставляет медиа проигрыватель.
В этом примере, мы подсоединили производитель и затем запустили потребителя. Затем, мы подождали завершения потребителя (в нашем случае, мы просто закрыли окно проигрывателя) и закрыли потребителя, производителя и фабрику перед выходом из приложения.
Следует отметить, что потребитель является многопоточным, ожидание некоторого события всегда требуется только после запуска и тольео перед остановкой или закрытием потребителя.
Также следует отметить, что можно переопределить значения по умолчанию:
$ MLT_CONSUMER=westley ./hello file.avi
Таким образом мы создадим
westley xml документ на стандартном выводе.
$ MLT_CONSUMER=westley MLT_PRODUCER=avformat ./hello file.avi
Таким образом мы проиграем видео напрямую через производитель
avformat, что позволит нам обойти функции нормализации.
$ MLT_CONSUMER=libdv ./hello file.avi > /dev/dv1394
Такой вариант выполнит конвертацию файла
file.avi в формат
DV и отправит контент на ваше
DV устройство.
Фабрики
Как показано в примере "Hello World", фабрики применяются для создания сервисных объектов.
Сама библиотека не предоставляет никаких сервисов сервисы предоставляются в виде плагинов. Плагин это организованная форма (модуль) и модуль можут предоставлять множество сервисов различного типа.
После инициализации фабрики все сконфигурированные сервисы готовы к использованию.
Полный набор методов принадлежащих фабрике показан ниже:
int mlt_factory_init( char *prefix );
const char *mlt_factory_prefix( );
char *mlt_environment( char *name );
mlt_producer mlt_factory_producer( char *name, void *input );
mlt_filter mlt_factory_filter( char *name, void *input );
mlt_transition mlt_factory_transition( char *name, void *input );
mlt_consumer mlt_factory_consumer( char *name, void *input );
void mlt_factory_close( );
Метод
mlt_factory_prefix возвращает путь к каталогу с инсталлированными модулями. Путь может быть определён с помощью метода
mlt_factory_init или может быть задан через переменную окружения
MLT_REPOSITORY. Если путь не определён, то используется значение по умолчанию
$(INSTALLDIR)/shared/mlt/modules.
Метод
mlt_environment предоставляет доступ только на чтение к коллекции пар
name=value, как показано в таблице приведённой ниже:
| Переменная | Описание | Значения |
| MLT_NORMALISATION | Система цветности | PAL или NTSC |
| MLT_PRODUCER | Производитель по умолчанию | fezzik или другой |
| MLT_CONSUMER | Потребитель по умолчанию | sdl или другой |
| MLT_TEST_CARD | Производитель для тестовой карты по умолчанию | любой |
Эти значения инициализируются из переменных окружения имеющих такие же имена.
Как показано выше, производитель может быть создан с помощью производителя "нормализация по умолчанию" и они могуть быть запрошены по имени. Фильтры и переходы всегда запрашиваются по имени, для них не существует значения по умолчанию.
Свойства сервиса
Как показано в документе
services.txt, у каждого сервиса есть свой собственный набор свойств, через которые можно управлять поведением сервиса.
Для того чтобы установить свойства сервиса следует получить соответствующий объект свойств. Для производителя это делается следующим образом:
mlt_properties properties = mlt_producer_properties( producer );
Все сервисы имеют соответствующий метод.
После получения данного объекта, все операции со свойствами сервиса производятся напрямую с этим объектом, например:
mlt_properties_set( properties, "name", "value" );
Более детальное описание объекта свойств будет приведено далее.
Списки проигрывания
Таким образом, мы показали простой пример. Следующим шагом мы организуем производителей в список проигрывания.
Давайте предположим, что мы адаптируем предыдущий пример и пожелаем проиграть по очереди несколько файлов, т.е.:
hello *.avi
Вместо прямого вызова метода
mlt_factory_producer мы создадим новую функцию
create_playlist. Эта функция будет создавать список проигрывания, создавая каждого производителя и добавляя его к списку.
mlt_producer create_playlist( int argc, char **argv )
{
// We're creating a playlist here
mlt_playlist playlist = mlt_playlist_init( );
// We need the playlist properties to ensure clean up
mlt_properties properties = mlt_playlist_properties( playlist );
// Loop through each of the arguments
int i = 0;
for ( i = 1; i < argc; i ++ )
{
// Create the producer
mlt_producer producer = mlt_factory_producer( NULL, argv[ i ] );
// Add it to the playlist
mlt_playlist_append( playlist, producer );
// Close the producer (see below)
mlt_producer_close( producer );
}
// Return the playlist as a producer
return mlt_playlist_producer( playlist );
}
Следует отметить, что мы закрыли производителя после его добавления. В действительности, то что мы делаем - это закрытие нашей ссылки к нему, т.к. список проигрывания создаёт свою собственную ссылку на производителя и закроет эту ссылку во время удаления списка проигрывания.
Такая функциональность ссылок была введена в версии
0.1.2. Данная функциональность 100% совместима с ранним механизмом регистрации ссылки и деструктора в свойствах объекта списка проигрывания.
При добавлении нескольких экземпляров одного производителя будут создано соответствующее количество ссылок на него.
Теперь, мы заменяем эти строчки в функции
main:
// Create a normalised producer
mlt_producer world = mlt_factory_producer( NULL, argv[ 1 ] );
на:
// Create a playlist
mlt_producer world = create_playlist( argc, argv );
и мы теперь будем проигрывать список клипов.
Фильтры
Вставка фильтров между производителем и потребителем производится следующим образом. Сначала фильтр подключается к производителю, затем к предыдущему фильтру, а последний фильтр к потребителю.
Пример:
// 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 producer
mlt_producer producer = mlt_factory_producer( NULL, clip );
// Get the service object of the producer
mlt_producer service = mlt_producer_service( producer );
// Create a filter
mlt_filter filter = mlt_factory_filter( "greyscale" );
// Create a playlist
mlt_playlist playlist = mlt_playlist_init( );
// Attach the filter to the producer
mlt_service_attach( producer, filter );
// Construct a playlist with various cuts from the producer
mlt_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 now
mlt_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 );
Естественно, что список проигрывания, будучи производителем, может быть помещён в другой список проигрывания, а фильтры могут быть подключены к медиа контенту или к созданному новому списку и так далее.
The main advantage of attached filters is that they remain attached and don't suffer from the maintenance problems associated with items being inserted and displacing calculated in/out points - this being a major issue if you exclusively use the connect or insert detached filters in a multitrack field (описано далее).
Введение в микширование
Микширование является простейшим способом использования перехода между соседними клипами в списке проигрывания.
Рассмотрим следующий список проигрывания:
+-+----------------------+----------------------------+-+
|X|A |B |X|
+-+----------------------+----------------------------+-+
Предположим, что "X" является "чёрным клипом" длиной в 50 кадров.
Когда вы запустите проигрывание этого списка, вы получите 50 кадров чёрного, которое резко переключится в клип "A", затем следует переключение на клип "B" и, наконец, опять 50 кадров чёрного.
Намерение состоит в том, чтобы преобразовать список проигрывания похожий на это:
+-+---------------------+-+------------------------+-+
|X|A |A|B |B|
|A| |B| |X|
+-+---------------------+-+------------------------+-+
Причем клип, относящийся к двум клипам, представляет собой переход. Следует отметить, что представление второго списка проигрывания короче чем первого, это очевидно, один 50 кадровый переход между двумя клипами уменшает время проигрывания на 50 кадров.
Микширование производится с помощью метода
mlt_playlist_mix. Предположим, что у вас есть список проигрывания подобный описанному выше и чтобы произвести первое микширование, необходимо сделать следующее:
// Create a transition
mlt_transition transition = mlt_factor_transition( "luma", NULL );
// Mix the first and second clips for 50
mlt_playlist_mix( playlist, 0, 50, transition );
// Close the transition
mlt_transition_close( transition );
Этот код выполнит первое микширование. Таким же образом производится микширование остальных клипов. В результате мы получим новый клип в списке проигрывания, соответственно следующим микшированием будет микширование третьего и четвёртого клипов.
Подсказка, для упрощения определение номера следующего клипа можно сделать так:
// Get the number of clips on the playlist
int i = mlt_playlist_count( );
// Iterate through them in reverse order
while ( i -- )
{
// Create a transition
mlt_transition transition = mlt_factor_transition( "luma", NULL );
// Mix the first and second clips for 50
mlt_playlist_mix( playlist, i, 50, transition );
// Close the transition
mlt_transition_close( transition );
}
Существуют также другие методы, например, использование метода
mlt_playlist_join для объединения текущего клипа и ещё одного (вы можете определить был ли новый клип создан или нет с помощью сравнения длины списка проигрывания до и после вызова метода).
Во время работы метод
mlt_playlist_mix генерирует трактор и мнультитрек, как будет описано ниже. Подобно подключаемым фильтрам, микширование упрощает жизнь при вставке элементов в список проигрывания.
Также следует отметить, что микширование позволяет упростить пользовательский интерфейс, вместо упорного использования сложного многотрекового объекта, вы можете выполнять множество действий на одном треке. Следовательно, дополнительные треки могут быть использованы для дублирования звуковой дорожки, микширования или композиции, которые позиционируются независимо и не подвергаются влиянию при работе с другими треками. Но если вам требуется претенциозный, запутывающий и расстраивающий интерфейс, то эта функциональность тоже поддерживается ;-).
Практичность и оптимизация
В предыдущих двух секциях я описал некоторую мощную функциональность разработанную для упрощения использования библиотеки MLT. Тем не менее, важно пояснить, что происходит когда вы пытаетесь вставить переход между двумя частями одного и того же видеоматериала.
Любой, кто знаком с компрессией видео, будет знать, что быстрый переход по материалу не всегда обходится без последствий с точки зрения производительности. Таким образом, если вам требуется получить два кадра одного клипа для перехода, это может привести к значительной нагрузке, что будет очень неприятно, особенно, если вы выполняете работу в реальном времени.
Как же это преодолеть?
В действительности, это очень просто, вызываете метод
mlt_producer_optimise для объекта верхнего уровня после модификации и библиотека сама определит как его обработать. Метод определяет максимальное число перекрывающихся экземпляров внутри объекта, создаёт необходимое количество клонов и назначает клонам соответствующие индексы.
В приведённом выше примере, вы просто вызываете:
// Optimise the playlist
mlt_producer_optimise( mlt_playlist_producer( playlist ) );
после выполнения микширования. Следует отметить, что вызов этого метода автоматически выполняется для
deserialised westleys.
Мультитрековость и Переходы
Поддержка мультитрековости требует следующее:
1. Необходимо, чтобы производитель и потребитель взаимодействовали между собой через один кадр;
1. Возможность сериализации и работы через "сеть" (или граф фильтра).
Мы можем отобразить мультитрековость стандартным способом:
+-----------------+ +-----------------------+
0: |a1 | |a2 |
+---------------+-+--------------------------+-+---------------------+
1: |b1 |
+------------------------------+
Перекрывающиеся области треков 0 и 1 должны использовать некоторый переход. Без использования перехода, кадры от
b1 и
a2 будут отображаться на время перекрытия треков (по умолчанию, трек с наибольшим номером получает преимущество в отображении).
Библиотека MLT имеет мультитрековый объект, но он не является производителем, в том смысле, что он может быть напрямую подключен к потребителю и всё будет работать. Потребитель будет рассматривать его как обычного производителя и, с точки зрения мультитрековости, вы не увидите ничего с трека 1, кроме переходов между клипами, промежуток между
a1 и
a2 будет заполнен тестовыми кадрами.
Такое случается из-за того, что потребитель получает по одному кадру от производителя, в то в ремя как мультитрек предоставляет один кадр для каждого трека. Что-то где-то должно удостоверяться, что все кадры получены от мультитрекового объекта и выбирать правильный кадр.
Следовательно, библиотека предоставляет прослойку для мультитрека, который называется
трактором. Задаче трактора является отслеживание того, что треки получаются, кадр выбирается нужный и что он себя ведёт как производитель.
Таким образом, мультитрек отдаёт поток трактору как показано ниже:
+----------+
|multitrack|
| +------+ | +-------+
| |track0|-|--->|tractor|
| +------+ | |\ |
| | | \ |
| +------+ | | \ |
| |track1|-|--->|---o---|--->
| +------+ | | / |
| | | / |
| +------+ | |/ |
| |track2|-|--->| |
| +------+ | +-------+
+----------+
Имея такую комбинацию, мы теперь можем подсоединять мультитреки к потребителям. The last non-test card will be retrieved and passed on.
Треки могут быть производителями, списками проигрывания и даже другими тракторами.
Теперь мы желаем вставить фильтры и переходы между мультитреком и трактором. Это можно сделать напрямуя с помощью вставки фильтров между ними. Но такой метод потребует большого количества подсоединений между левыми и правыми производителями и потребителями, т.е. мы должны иметь возможность автоматизировать данный процесс.
Так в соответствии с нашей сельскохозяйственной темой, родилось понятие "поля". Мы "выращиваем" фильтры и переходы в поле, и трактор тянет мультитрек (подумайте о комбайне :-)) по полю и производит кадр.
Концептуально, мы можем видеть это так:
+----------+
|multitrack|
| +------+ | +-------------+ +-------+
| |track0|-|--->|field |--->|tractor|
| +------+ | | | |\ |
| | | filters | | \ |
| +------+ | | and | | \ |
| |track1|-|--->| transitions |--->|---o---|--->
| +------+ | | | | / |
| | | | | / |
| +------+ | | | |/ |
| |track2|-|--->| |--->| |
| +------+ | +-------------+ +-------+
+----------+
Таким образом, на следует сначала создать трактор, через который мы получаем мультитрек и остальные объекты. Мы можем поработать с этими объектами и затем подключить трактор к потребителю.
А вот так это выглядит со стороны потребителя:
+-----------------------------------------------+
|tractor +--------------------------+ |
| +----------+ | +-+ +-+ +-+ +-+ | |
| |multitrack| | |f| |f| |t| |t| | |
| | +------+ | | |i| |i| |r| |r| | |
| | |track0|-|--->| |l|- ->|l|- ->|a|--->|a|\| |
| | +------+ | | |t| |t| |n| |n| | |
| | | | |e| |e| |s| |s| |\ |
| | +------+ | | |r| |r| |i| |i| | \|
| | |track1|-|- ->| |0|--->|1|--->|t|--->|t|-|--o--->
| | +------+ | | | | | | |i| |i| | /|
| | | | | | | | |o| |o| |/ |
| | +------+ | | | | | | |n| |n| | |
| | |track2|-|- ->| | |- ->| |--->|0|- ->|1|/| |
| | +------+ | | | | | | | | | | | |
| +----------+ | +-+ +-+ +-+ +-+ | |
| +--------------------------+ |
+-----------------------------------------------+
Надеемся, что пример прояснит всё.
Предположим, что нам необходимо выводить некую метку в нашем примере. Мы уже расширили код примера для проигрывания нескольких клипов и теперь добавим текстовую метку "Hello World" в верхний левый угол кадра:
mlt_producer create_tracks( int argc, char **argv )
{
// Create the tractor
mlt_tractor tractor = mlt_tractor_new( );
// Obtain the field
mlt_field field = mlt_tractor_field( tractor );
// Obtain the multitrack
mlt_multitrack multitrack = mlt_tractor_multitrack( tractor );
// Create a composite transition
mlt_transition transition = mlt_factory_transition( "composite", "10%,10%:15%x15%" );
// Create track 0
mlt_producer track0 = create_playlist( argc, argv );
// Create the watermark track - note we NEED fezzik for scaling here
mlt_producer track1 = mlt_factory_producer( "fezzik", "pango" );
// Get the length of track0
mlt_position length = mlt_producer_get_playtime( track0 );
// Set the properties of track1
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 );
// Now set the properties on the transition
properties = mlt_transition_properties( transition );
mlt_properties_set_position( properties, "in", 0 );
mlt_properties_set_position( properties, "out", length - 1 );
// Add our tracks to the multitrack
mlt_multitrack_connect( multitrack, track0, 0 );
mlt_multitrack_connect( multitrack, track1, 1 );
// Now plant the transition
mlt_field_plant_transition( field, transition, 0, 1 );
// Close our references
mlt_producer_close( track0 );
mlt_producer_close( track1 );
mlt_transition_close( transition );
// Return the tractor
return mlt_tractor_producer( tractor );
}
Всё, что нам теперь необходимо - заменить эти строчки в функции
main:
// Create a playlist
mlt_producer world = create_playlist( argc, argv );
на:
// Create a watermarked playlist
mlt_producer world = create_tracks( argc, argv );
и у нас есть средство для проигрывания клипов с навязчивой меткой - только этого нам и не хватало, не так ли?
Тот же самый результат можно достичь с помощью простого фильтра
watermark, вставленным между производителем и потребителем.
Секция 3 - Структура и дизайн
Иерархия класса
Инфраструктура библиотеки MLT имеет иерархию ОО класса, который состоит из следующих публичных классов и абстракций:
mlt_properties
mlt_frame
mlt_service
mlt_producer
mlt_playlist
mlt_tractor
mlt_filter
mlt_transition
mlt_consumer
mlt_deque
mlt_pool
mlt_factory
Каждый класс указанный выше может рассматриваться как расширение классов, описанных выше и левее.
Следующие секции описывают функциональность предоставляемой инфраструктуры. Это ключевые компоненты, понимание которых необходимо для работы с библиотекой.
Класс mlt_properties
Класс свойств является базовым классом для классов кадра и сервиса.
Он разработан для организации эффективной таблицы переходов для различных типов информации, таких как строки, целые, числа с плавающей точкой, указатели на данные и структуры данных.
Каждое свойство имеет уникальное название.
Обычное использование класса:
// 1. Create a new, empty properties set;
mlt_properties properties = mlt_properties_new( );
// 2. Assign the value "world" to the property "hello";
mlt_properties_set( properties, "hello", "world" );
// 3. Retrieve and print the value of "hello";
printf( "%s\n", mlt_properties_get( properties, "hello" ) );
// 4. Reassign "hello" to "world!";
mlt_properties_set( properties, "hello", "world!" );
// 5. Retrieve and print the value of "hello";
printf( "%s\n", mlt_properties_get( properties, "hello" ) );
// 6. Assign the value "0" to "int";
mlt_properties_set( properties, "int", "0" );
// 7. Retrieve and print the integer value of "int";
printf( "%d\n", mlt_properties_get_int( properties, "int" ) );
// 8. Assign the integer value 50 to "int2";
mlt_properties_set_int( properties, "int2", 50 );
// 9. Retrieve and print the double value of "int2";
printf( "%s\n", mlt_properties_get( properties, "int2" ) );
Шаги со второго по пятый демонстрируют уникальность имени свойства, набор операций над существующим "именем" изменяют его значение. Они также освобождают память, ассоциированную с предыдущим значением. Следует отметить, что таким же образом можно изменить тип значения.
Шаги с шест