Библиотека сайта rus-linux.net
Фреймворк GStreamer. Руководство разработчика плагинов. Резервирование памяти
Оригинал: GStreamer Plugin Writer's GuideАвторы: Richard John Boulton, Erik Walthinsen, Steve Baker, Leif Johnson, Ronald S. Bultje, Stefan Kost, Tim-Philipp Muller, Wim Taymans
Дата публикации: 19 июля 2014 г.
Перевод: А.Панин
Дата перевода: 27 июля 2014 г.
Глава 15. Резервирование памяти
Темы резервирования и управления памятью являются очень важными при рассмотрении программных компонентов, предназначенных для обработки мультимедийных данных. Ведь при воспроизведении видео высокой четкости для хранения отдельных кадров используются многомегабайтные буферы. При этом важно повторно использовать зарезервированные фрагменты памяти там, где это возможно, а не постоянно резервировать и освобождать новые фрагменты памяти.
Мультимедийные системы обычно используют чипы специального назначения, такие, как цифровые аудиопроцессоры (DSPs) или графические процессоры (GPUs) для выполнения ресурсоемких операций (особенно в случае работы с видеоданными). Эти чипы специального назначения обычно предъявляют жесткие требования к памяти, с которой они могут работать, а также к методам доступа к этой памяти.
В данной главе описываются функции управления памятью, которые могут использоваться как самим фреймворком GStreamer, так и его плагинами. В первую очередь мы обсудим низкоуровневый объект памяти типа GstMemory
, который управляет доступом к фрагменту памяти. После этого мы продолжим обсуждение механизмов управления памятью, рассмотрев объект буфера данных типа GstBuffer
, который используется при осуществлении обмена данными между плагинами (и приложением) и использует объект памяти типа GstMemory
. Мы поговорим об объекте метаданных типа GstMeta
, который может размещаться в буферах данных для передачи дополнительной информации об этих буферах и использованной для них памяти. Для эффективного управления буферами данных одинакового размера используется объект пула буферов данных типа GstBufferPool
, который мы также рассмотрим. В заключении данной главы мы также рассмотрим запрос согласования резервирования буферов данных GST_QUERY_ALLOCATION, который используется для согласования режимов управления памятью между элементами.
15.1. Объект памяти типа GstMemory
Класс GstMemory
лежит в основе объекта, который управляет фрагментом памяти. Объект памяти указывает на фрагмент памяти максимального размера "maxsize". Фрагмент памяти размещается со сдвигом "offset", при этом "size" байт из фрагмента памяти доступны. Максимальный размер фрагмента памяти не может быть изменен ни при каких обстоятельствах после создания объекта памяти, однако, сдвиг и размер фрагмента памяти могут быть изменены.
15.1.1. Объект для резервирования памяти типа GstAllocator
Объекты памяти типа GstMemory
создаются с помощью объекта для резервирования памяти типа GstAllocator
. Большинство объектов для резервирования памяти реализует стандартный метод gst_allocator_alloc ()
, но некоторые объекты для резервирования памяти могут реализовывать отличный метод резервирования памяти, например такой, который требует дополнительных параметров для резервирования определенного фрагмента памяти.
Различные объекты резервирования памяти существуют, например, для резервирования системной памяти, разделяемой памяти и памяти, используемой для функционирования механизма разделения буферов между устройствами под названием DMAbuf с использованием файлового дескриптора. Для реализации поддержки нового типа памяти вам придется реализовать новый объект для резервирования памяти таким образом, как будет показано ниже.
15.1.2. Пример использования API объекта памяти типа GstMemory
Доступ к данным из фрагмента памяти, созданного с использованием объекта памяти типа GstMemory
, всегда защищен с помощью пары функций для управления отображением этого фрагмента памяти gst_memory_map()
и gst_memory_unmap()
. Режим доступа (чтение/запись) должен быть задан при отображении фрагмента памяти. Функция отображения фрагмента памяти возвращает указатель на зарезервированный фрагмент памяти, доступ к которому может осуществляться в соответствии с установленным режимом доступа.
Ниже приведен пример создания объекта памяти типа GstMemory
и использования функции gst_memory_map()
для непосредственного доступа к фрагменту памяти.
15.1.3. Реализация класса для резервирования памяти на основе базового класса GstAllocator
Работа над данным разделом руководства пока не начата.
15.2. Объект буфера данных типа GstBuffer
Объект буфера данных типа GstBuffer
является легковесным объектом, который передается между элементами по направлению конвейера и содержит фрагмент памяти с данными и метаданные. Он представляет мультимедийные данные, которые передаются или запрашиваются элементами, расположенными по направлению конвейера.
Буфер данных содержит один ли несколько объектов памяти типа GstMemory
, которые представляют данные буфера.
- Меток времени DTS и PTS. Эти метки времени представляют время декодирования и вывода содержимого буфера соответственно и используются элементами для синхронизации своей работы и планирования процесса передачи буферов данных. Обе метки времени могут иметь значение GST_CLOCK_TIME_NONE в том случае, если время неизвестно/не определено.
- Значения продолжительности воспроизведения содержимого буфера. Это значение может быть равно GST_CLOCK_TIME_NONE в том случае, если продолжительность воспроизведения неизвестна/не установлена.
- Специфичных для мультимедийных данных значений сдвигов offset и offset_end. Для видео с помощью этих значений задается номер кадра в потоке, а для аудио - номер сэмпла. При работе с другими типами мультимедийных потоков значения могут использоваться для других целей.
- Произвольных структур, создаваемых на основе объекта метаданных типа
GstMeta
, о котором будет сказано ниже.
15.2.1. Возможность записи в буфер данных, представленный объектом типа GstBuffer
Данные могут записываться в буфер в том случае, если значение счетчика ссылок объекта буфера данных равно 1, что значит, что только один объект удерживает ссылку на этот буфер. Вы можете модифицировать любые данные в буфере данных только в том случае, если запись в этот буфер разрешена. Это означает, что вы должны вызвать функцию gst_buffer_make_writable()
перед изменением меток времени, сдвигов, метаданных или добавлении и удалении блоков памяти.
15.2.2. Примеры использования API объекта буфера данных типа GstBuffer
Вы можете создать буфер данных с помощью функции gst_buffer_new ()
, после чего добавить в него объекты памяти, либо использовать вспомогательную функцию gst_buffer_new_allocate ()
, которая комбинирует два описанных действия. Также возможно создание объекта буфера данных для уже существующего объекта памяти с помощью функции gst_buffer_new_wrapped_full ()
, причем вы также можете передать указатель на функцию, которую следует вызвать в тот момент, когда память должна быть освобождена.
Вы можете получить доступ к фрагментам памяти из буфера данных, получая доступ и отображая объекты памяти типа GstMemory
по отдельности или одновременно с помощью функции gst_buffer_map ()
. Данная функция объединит все фрагменты памяти в один большой блок и передаст вам указатель на этот блок.
Ниже приведен пример создания буфера данных и доступа к его фрагментам памяти.
15.3. Объект метаданных типа GstMeta
С помощью системы, основанной на объектах метаданных типа GstMeta
вы можете добавлять произвольные структуры в буферы данных. Эти структуры описывают такие дополнительные параметры буфера данных, как параметры обрезки изображения, перехода, интересующей области изображения и.т.д.
В рамках системы метаданных производится разделение между спецификацией API (описывающей, чем являются метаданные и как выглядит API) и его реализацией (обуславливающей принцип работы системы). Это обстоятельство делает возможным создание различных реализаций одного и того же API, например, в зависимости от используемого аппаратного обеспечения.
15.3.1. Пример использования API объекта метаданных типа GstMeta
После резервирования нового буфера данных вы можете добавить метаданные в этот буфер с помощью функций API, специфичного для выбранного типа метаданных. Это значит, что для использования API вам придется подключить заголовочный файл, в котором объявлены структуры метаданных.
В соответствии с соглашением, API системы метаданных с именем FooBar
должен предоставлять два метода: gst_buffer_add_foo_bar_meta ()
и gst_buffer_get_foo_bar_meta ()
. Обе функции должны возвращать указатель на структуру типа FooBarMeta
, которая содержит поля метаданных. Некоторые функции _add_*_meta ()
могут иметь дополнительные параметры, которые обычно используются для индивидуального задания значений полей структуры метаданных.
Давайте рассмотрим структуру метаданных, используемую для указания параметров области обрезки кадров видео.
После этого элемент может использовать метаданные из объекта буфера в процессе вывода кадра следующим образом:
15.3.2. Реализация нового класса метаданных на основе базового класса GstMeta
В нескольких следующих разделах мы продемонстрируем способ добавления поддержки нового типа метаданных в систему метаданных и использования его при передаче буферов.
15.3.2.1. Описание API метаданных
В первую очередь мы должны описать наш API, причем нам придется зарегистрировать этот API в рамках системы метаданных. Это важно, так как данное описание API будет использоваться в процессе согласования элементами типов метаданных, которыми они будут обмениваться. Описание API также содержит произвольные тэги, сообщающие о том, какую информацию содержат метаданные. Они важны при исследовании способа передачи метаданных в процессе передачи буферов в рамках конвейера.
В том случае, если вы создаете новую реализацию существующего API, вы можете пропустить этот этап и перейти непосредственно к этапу реализации.
Мы начнем работу с создания заголовочного файла my-example-meta.h
, который будет содержать описание API и структуры для хранения наших метаданных.
Описание API метаданных состоит из описания структуры, которая содержит целочисленную переменную типа gint и строковую переменную. Первое поле структуры должно иметь тип GstMeta
.
Также мы должны объявить функцию my_example_meta_api_get_type ()
, которая будет регистрировать описание нашей реализации API метаданных. Кроме того, мы объявим вспомогательный макрос gst_buffer_get_my_example_meta ()
, который будет просто искать и возвращать структуру метаданных, созданную с использованием нашего нового API.
Теперь рассмотрим способ реализации функции my_example_meta_api_get_type ()
из файла my-example-meta.c
.
Как вы видите, она просто использует функцию gst_meta_api_type_register ()
для регистрации имени API и нескольких тэгов. В качестве результата возвращается новый указатель типа GType, который представляет только что зарегистрированный API.
15.3.2.2. Реализация API метаданных
Теперь мы можем приступить к реализации нашего зарегистрированного в форме типа GType API метаданных. Подробности реализации API метаданных хранятся в рамках структуры GstMetaInfo
, которая будет доступна для пользователей реализации вашего API метаданных посредством функции my_example_meta_get_info ()
и вспомогательного макроса MY_EXAMPLE_META_INFO
. Вы также должны будете создать метод для добавления реализации структуры, хранящей ваши метаданные, в объект буфера данных типа GstBuffer
. В ваш заголовочный файл my-example-meta.h
потребуется добавить несколько дополнительных строк:
Давайте рассмотрим реализацию описанных функций из файла my-example-meta.с
.
Функция gst_meta_register ()
регистрирует такие параметры реализации, как название реализованного вами API и размер структуры для хранения метаданных вместе с методами для инициализации и освобождения области памяти. Вы также можете реализовать функцию преобразования, которая будет вызываться тогда, когда происходит определенное преобразование данных буфера (идентифицируемое с помощью ассоциации типа quark между целочисленным значением специфичными данными).
Наконец, вы должны реализовать функцию gst_buffer_add_*_meta()
, которая будет добавлять объект метаданных в объект буфера данных и устанавливать значения полей структуры метаданных.
15.4. Объект пула буферов данных типа GstBufferPool
Объект типа GstBufferPool
является экземпляром базового класса для управления списками буферов данных многоразового использования. Важная особенность функционирования данного объекта состоит в необходимости использования буферов данных с одними и теми же параметрами, такими, как размер, дополнение, метаданные и сдвиг.
Объект пула буферов данных может быть настроен для управления минимальным и максимальным количеством буферов данных определенного размера. Также этот объект может быть настроен для использования определенного объекта для резервирования памяти типа GstAllocator
, с помощью которого будут резервироваться фрагменты памяти для буферов данных. Объект пула буферов данных поддерживает специфичные возможности, такие, как добавление объектов метаданных типа GstMeta
в объекты буферов данных, находящиеся в пуле, а также заполнение данными определенных фрагментов памяти буферов данных.
Объект пула буферов данных может быть как активным, так и неактивным. В неактивном состоянии вы можете осуществлять настройку объекта. В активном состоянии вы больше не сможете изменить настройки объекта, но вы сможете извлекать буферы данных из пула и добавлять их в пул.
В следующих разделах мы рассмотрим методику использования объекта пула буферов данных.
15.4.1. Пример использования API объекта пула буферов данных типа GstBufferPool
Может существовать множество различных реализаций пулов буферов данных; эти реализации являются подклассами базового класса GstBufferPool
. В данном примере мы будем считать, что мы каким-либо образом получили доступ к пулу буферов данных, либо благодаря тому, что создали его самостоятельно, либо благодаря приему ответа на запрос согласования стратегии резервирования буферов данных ALLOCATION таким образом, как будет показано ниже.
Пул буферов данных изначально находится в неактивном состоянии, поэтому мы можем настроить его. Попытка настройки пула буферов данных, находящегося в отличном от неактивного состоянии закончится неудачей. Аналогично, попытка активации не настроенного пула буферов данных также закончится неудачей.
Настройки пула буферов данных хранятся в стандартной структуре типа GstStructure
, которая может быть получена с помощью функции gst_buffer_pool_get_config()
. Существуют вспомогательные методы для получения и установки значений параметров, хранящихся в этой структуре. После обновления значений полей структуры настройки пула буферов данных обновляются с помощью функции gst_buffer_pool_set_config()
.
- Возможностей резервируемых буферов данных.
- Размера буферов данных. Это рекомендуемый размер буферов данных в пуле. Пул может принять решение о резервировании буферов данных большего размера с целью заполнения фрагмента памяти дополнительными данными.
- Минимального и максимального количества буферов данных в пуле. В том случае, если в качестве минимального количества буферов данных устанавливается значение > 0, пул буферов данных предварительно зарезервирует установленное количество буферов данных. В том случае, если максимальное количество буферов данных не равно 0, пул буферов данных будет резервировать буферы данных до достижения максимального установленного значения.
- Объекта резервирования памяти и его параметров. Некоторые пулы буферов могут игнорировать указанный объект резервирования буферов и использовать встроенный механизм резервирования памяти.
- Других произвольных параметров пула буферов данных, идентифицируемых с помощью строк. Объект пула буферов данных выводит список поддерживаемых параметров при использовании функции
gst_buffer_pool_get_options()
, причем вы можете выяснить, поддерживается ли определенный параметр, воспользовавшись функциейgst_buffer_pool_has_option()
. Параметр может быть активирован путем добавления его идентификатора в структуру настроек с помощью функцииgst_buffer_pool_config_add_option ()
. Описанные параметры используются для активации таких возможностей, как, к примеру, передача метаданных вместе с буферами данных или добавление дополнительных настраиваемых параметров для заполнения фрагмента памяти данными.
После того, как значения настроек переданы объекту пула буферов данных, последний может быть активирован посредством вызова функции gst_buffer_pool_set_active (pool, TRUE)
. С этого момента вы сможете использовать функцию gst_buffer_pool_acquire_buffer ()
для получения буфера данных из пула аналогичным образом:
Важно проверять значение, возвращаемое функцией получения буфера данных, так как ее вызов может завершиться неудачей: в момент прекращения работы вашего элемента пул буферов будет деактивирован, после чего все вызовы функции получения буфера данных будут возвращать значение GST_FLOW_FLUSHING.
Все буферы данных, которые были получены из пула, будут иметь связь с оригинальным пулом. В тот момент, когда последняя ссылка на буфер данных будет удалена, фреймворк GStreamer автоматически вызовет функцию gst_buffer_pool_release_buffer()
для перемещения буфера данных назад в пул. Вы (или любой другой элемент, расположенный по направлению буфера) не должны знать о том, из какого пула был получен буфер данных, вам достаточно всего лишь удалить ссылку на этот буфер данных.
15.4.2. Реализация нового класса пула буферов данных на основе базового класса GstBufferPool
Работа над данным разделом руководства пока не начата.
15.5. Запрос согласования стратегии резервирования буферов данных GST_QUERY_ALLOCATION
Запрос стратегии резервирования буферов данных ALLOCATION используется для согласования использования объектов метаданных типа GstMeta
, объектов пулов буферов данных типа GstBufferPool
и объектов для резервирования памяти типа GstAllocator
между элементами. Согласование стратегии резервирования памяти всегда инициируется и осуществляется на уровне выходной точки соединения элемента после согласования формата потока данных и перед принятием решения о передаче буферов данных. На уровне входной точки соединения элемента может быть высказано пожелание относительно используемой стратегии резервирования памяти, но конечное решение все равно будет принято на уровне выходной точки соединения элемента, хотя оно и будет базироваться на предложениях, поступивших от входной точки соединения элемента, расположенного по направлению конвейера.
- Массив предлагаемых поддерживаемых объектов пулов буферов данных на основе класса
GstBufferPool
с предлагаемыми размерами, минимальным и максимальным количеством буферов данных. - Массив объектов для резервирования памяти типа GstAllocator вместе с такими предлагаемыми параметрами резервирования памяти, как флаги, префикс, выравнивание и дополнение. Эти объекты для резервирования памяти могут также быть настроены для работы с рамках пула буферов данных в том случае, если такой режим работы поддерживается пулом буферов данных.
- Массив поддерживаемых реализаций объектов метаданных типа
GstMeta
вместе со специфичными параметрами объектов метаданных. Важно, чтобы расположенный против направления конвейера элемент обладал информацией о том, какой тип метаданных поддерживается расположенным по направлению конвейера элементом перед тем, как он будет помещать объекты с этими метаданными в буферы данных.
После того, как возвращается ответ на запрос согласования стратегии резервирования буферов данных GST_QUERY_ALLOCATION, на уровне выходной точки соединения элемента может быть осуществлен выбор из доступных вариантов пулов буферов данных, объектов для резервирования памяти и объектов метаданных, обуславливающий используемую стратегию резервирования буферов.
15.5.1. Пример использования запроса согласования стратегии резервирования буферов данных ALLOCATION
Ниже приведен пример использования запроса согласования стратегии резервирования буферов данных ALLOCATION:
С помощью этой реализации будет создаваться специальный объект пула буферов данных типа GstVideoBufferPool
, который был реализован с целью резервирования буферов видеоданных. Вы также можете настроить пул буферов данных для размещения объектов метаданных типа GstVideoMeta
в буферах данных из этого пула путем вызова функции gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META)
.
15.5.2. Запрос согласования стратегии резервирования буферов данных ALLOCATION в базовых классах
- Функция
propose_allocation ()
должна использоваться для предложения параметров резервирования буферов данных расположенному против направления конвейера элементу. - Функция
decode_allocation ()
должна использоваться для выбора параметров резервирования буферов данных на основе предложений, полученных от расположенного по направлению конвейера элемента.
Реализации этих методов должны модифицировать объект запроса типа GstQuery
, обновляя параметры пула буферов данных и параметры резервирования памяти.
Следующий раздел : Типы и свойства потоков данных.