По определению, тренажерно-обучающие системы (ТОС) – разновидность технических систем, позволяющих решать задачи подготовки персонала с целью обучения управлению сложными техническими системами, в том числе в условиях, создающих угрозу жизни (агрессивные среды) или принудительно не реализуемых в реальной среде. Кроме формирования индивидуальных профессиональных навыков и умений, ТОС могут применяться для отработки групповых операций.
Подсистема визуализации обеспечивает отображение результатов моделирования внешней среды и объекта управления с помощью устройств отображения информации. Отображение разнородных видеоматериалов в виртуальной трехмерной сцене является одним из требований к ТОС.
Для отображения видеоматериалов в подсистеме визуализации была выбрана кросс-платформенная система декодирования видеофайлов FFmpeg, распространяемая по лицензии LGPL. Пакет FFmpeg не предоставляет возможностей для визуализации декодированных видеоизображений и воспроизведения звуковых данных. Для воспроизведения звуковых данных из декодируемого видеофайла был использован пакет SFML, распространяемый по лицензии zlib/png. В качестве основы подсистемы визуализации ТОС использован графический движок Horde3D, распространяемый по лицензии EPL.
Воспроизведение видеоматериалов внутри виртуальной трехмерной сцены является сложной задачей, поскольку необходимо учитывать факторы, не актуальные при воспроизведении в медиа- плеерах. Графическая подсистема должна визуализировать трехмерную сцену с приемлемой частотой кадров (не менее 25 кадров в секунду) и при этом быть способной реагировать на внешние воздействия, в том числе на изменения параметров трехмерной сцены или загрузку дополнительных объектов.
Разработанная архитектура подсистемы воспроизведения видеоматериалов позволяет декодировать и отображать одновременно несколько видео высокой четкости в трехмерной сцене. В ее архитектуру входят: декодер видео, в котором происходит декодирование видео- и аудиопакетов; подсистема воспроизведения декодированного звука; управляющая структура, необходимая для запуска видео, паузы воспроизведения, выставления громкости воспроизводимого видео и т.д.; интерфейс взаимодействия с подсистемой визуализации, необходимый для обновления видеокадров.
Общая схема работы декодера показана на рисунке 1.
Во время инициализации декодера считываются все необходимые параметры о видеофайле: кодеки, используемые для сжатия видео- и аудиоинформации, количество звуковых и видеопотоков в файле, ширина и высота (разрешение) видео, количество каналов звука и т.д.
Размещение пакетов в файле зависит от формата видеофайла, поэтому сложно предсказать, в каком порядке будут следовать видео- и аудиопакеты. Это может привести к ситуации, когда очередь видеопакетов заполнена, а очередь аудио- пакетов пуста. Узнать тип следующего пакета невозможно до его чтения. Если следующий видеопакет окажется закодированным видеокадром, то его необходимо либо поместить в очередь видеопакетов, то есть превысить заданные рамки очереди, либо удалить, что приводит к артефактам на конечном изображении или пропущенным кадрам и рывкам воспроизводимого видео. Для решения этой проблемы было принято решение создать третью очередь пакетов, в которую помещаются пакеты различного типа из видеофайла, а затем сортируются по типу (аудио или видео) в одну из очередей. Сортировка осуществляется по методу FIFO («первый вошел, первый вышел»).
Видеодекодер проверяет, есть ли пакеты в очереди видеопакетов, и при их наличии забирает пакет из начала очереди. Процесс декодирования видеопакета происходит в два этапа: декодирование пакета и преобразование декодированного кадра в необходимую цветовую модель. После декодирования и преобразования в другую цветовую модель кадр помещается в очередь декодированных кадров. Аналогично аудиодекодер проверяет наличие пакетов в очереди и при наличии пакетов декодирует первый пакет из очереди. После декодирования звуковые данные в формате PCM помещаются в очередь декодированных звуковых данных.
Процесс декодирования ресурсоемкий. Так, декодирование одного видеопакета с разрешением 1280 на 720 (720p) на процессоре Intel Core 2 Quad 2,66 ГГц занимает от 5 до 10 мс, для видео размером 1920 на 1080 (1080p) – от 10 до 20 мс. Декодирование пакета звуковых данных занимает значительно меньше времени (0,5–2 мс), но если одновременно декодируется звук для нескольких воспроизводимых видео, это может серьезно снизить производительность. Скорость получения пакетов из видеофайла ограничивается производительностью системы хранения данных (СХД), что, в свою очередь, влияет на производительность подсистемы визуализации. Задержки образуются, если в момент считывания пакетов из видеофайла СХД обрабатывала другие команды либо файл являлся фрагментированным, что заставило бы СХД искать фрагменты данного файла. Для преодоления проблем с производительностью была использована многопоточность.
Многопоточность – свойство платформы (например операционной системы) или приложения, состоящее в том, что процесс, запущенный на платформе, может состоять из нескольких потоков, выполняющихся параллельно, то есть без предписанного порядка во времени. При выполнении некоторых задач такое разделение позволяет достичь более эффективного использования ресурсов вычислительной машины.
Для оптимизации процесса декодирования видео используются отдельные потоки для сбора пакетов, декодирования видео- и аудиопакетов. Процесс воспроизведения звука также происходит в дополнительном потоке. Для реализации многопоточности было принято решение интегрировать библиотеку управления потоками thread системы boost.
Система boost – это набор кросс-платформенных библиотек, расширяющих стандартные инструменты языка C++ и распространяемых по лицензии Boost Software License. Некоторые библиотеки являются кандидатами на включение в следующий стандарт языка C++.
Обновление видеотекстуры в подсистеме визуализации осуществляется в основном потоке программы (потоке рендеринга). Каждый кадр подсистемы визуализации проходит проверку – следует ли обновлять видеотекстуру в трехмерной сцене или нет. Если обновление требуется, видеокадр берется из очереди декодированных кадров и помещается в память видеокарты в качестве текстуры, которая может быть наложена на поверхность любого трехмерного объекта.
При запуске видео создается пустая текстура с размерами видеокадра, которая затем применяется к материалу трехмерного объекта (материал объекта определяет, какие текстуры и шейдеры использует данный трехмерный объект). Перед применением новой текстуры к материалу выполняется резервное копирование старого материала для возможности возвращения к исходным текстурам объекта после окончания воспроизведения видео. Для повышения производительности используются два так называемых пиксельных буфера, которые применяются для хранения пиксельных данных (текстуры) в видеопамяти и позволяют значительно снизить временные затраты на обновление видеотекстуры в подсистеме визуализации. При использовании стандартной функциональности движка для обновления текстур в видеопамяти данные сначала копируются из видеокадра в промежуточный массив, затем из массива помещаются в текстуру. При обновлении текстуры в видеопамяти сначала происходит выделение нового места под данные и только затем копирование данных из промежуточного массива в видеопамять. Все описанные действия выполняются центральным процессором, что значительно снижает производительность подсистемы визуализации. Например, при воспроизведении FullHD видео (1920 на 1080) время копирования данных из видеокадра в промежуточный массив составляет приблизительно 3–4 мс, а время обновления текстуры из промежуточного массива в видеопамяти составляет от 5 до 8 мс. Преимущество пиксельных буферов в том, что затраты процессорного времени необходимы только на помещение информации в буфер, а обновление текстуры из буфера происходит практически мгновенно (около 0,1 мс) за счет того, что обработка перемещения данных из пиксельного буфера в текстуру возлагается на видеокарту.
Проверка необходимости обновления видеотекстуры в подсистеме визуализации осуществляется поэтапно (рис. 2). Сначала определяется состояние видео: проигрывается, приостановлено, остановлено. Если видео остановлено или приостановлено, дальнейшие проверки не выполняются. Приостановленное видео отличается от остановленного тем, что все потоки декодирования созданы, но остановлены, а очереди пакетов и декодированных кадров заполнены; если видео остановлено, то оно загружено (декодер инициализирован, информация о видеофайле получена), но его потоки декодирования не созданы и очереди пакетов пусты.
Составной частью первой проверки является проверка на кэширование (выполняется или нет). В начале воспроизведения видео запускается процесс кэширования, который заполняет очереди пакетов декодера, декодированных звуковых данных и декодированных кадров. Кэширование пакетов и управление этим процессом происходят в потоке декодера, чтобы избежать проблем с производительностью (падением количества отображаемых подсистемой визуализации кадров в секунду), а декодирование аудио- и видеопакетов происходит в соответствующих потоках декодера. Во время кэширования данных основной поток приложения раз в итерацию цикла генерации кадров (далее ИЦ) подсистемы визуализации проверяет состояние кэшируемого видео. Если хотя бы один видеокадр уже был декодирован, то он помещается в пиксельный буфер, а затем удаляется из очереди декодированных кадров. После заполнения всех пиксельных буферов данного видео основной поток продолжает раз в кадр проверять состояние видео, пока процесс кэширования не будет завершен. После завершения этого процесса проис- ходит запуск аудиопотока и состояние видео меняется на «проигрывается». Так же запускается таймер, по которому отсчитывается время для проигрываемого видео.
После первой проверки выполняется вторая – на необходимость обновления видеотекстуры в подсистеме визуализации. В ходе данной проверки время, прошедшее с начала воспроизведения видео, сравнивается со временем, когда необходимо отобразить следующую видеотекстуру. Для правильного отображения видеотекстур в подсистеме визуализации был разработан следующий алгоритм:
ЕСЛИ: |Т1–Т4|£|Т2–Т4|
ТО: {необходимо обновить видеотекстуру в подсистеме визуализации;
Т4=Т4+Т5}
ИНАЧЕ: обновление видеотекстуры в подсистеме визуализации не требуется,
где T1 – время с начала воспроизведения видео (мс); T2 – предполагаемое время генерации следующего кадра подсистемы визуализации (с начала воспроизведения видео) (мс); T3 – время генерации текущего кадра подсистемы визуализации (мс) (для первого кадра после начала воспроизведения видео предполагаемое время следующего кадра равно времени генерации текущего кадра (T3); для расчета последующих значений применяются значения T3, которые сохраняются в массиве из 10 элементов; для расчета Т2 применяется среднее арифметическое значений из массива; при заполнении массива, хранящего значения Т3, первое значение удаляется); Т4 – время отображения следующей видеотекстуры (для первой отображаемой видеотекстуры оно равно Т5 (мс)); Т5 – 1/частоту кадров воспроизводимого видео (мс).
Независимо от результата описанной выше проверки после нее выполняется проверка на наполненность пиксельных буферов. Если хотя бы один пиксельный буфер не заполнен, в него добавляется новый видеокадр.
Если после второй проверки необходимо обновить видеотекстуру в подсистеме визуализации, это обновление происходит из соответствующего пиксельного буфера. Индекс используемого буфера определяется каждый раз при необходимости обновления текстуры. Так как для каждого видео предусмотрено 2 пиксельных буфера, то индекс следующего используемого буфера определяется по формуле: индекс следующего буфера = остаток от деления по модулю 2 суммы индекса предыдущего буфера и единицы.
При внедрении функциональности воспроизведения видео в подсистему визуализации возникли некоторые проблемы, основные из которых – рассинхронизация звука и видео во время воспроизведения и значительное снижение производительности при воспроизведении видео высокой четкости.
Алгоритм воспроизведения видео и синхронизации видео и звука в приложениях-медиаплеерах зачастую проще разработанного алгоритма, так как в этих приложениях фактор производительности менее значим. Наиболее часто применяемым методом синхронизации является синхронизация видео по звуку, то есть скорость воспроизведения звука считается постоянной величиной (определяется на основании указанной в видеофайле информации о частоте дискретизации и количестве каналов), а время отображения видеокадра рассчитывается на основании времени, записанного в пакетах, и времени, прошедшего с начала воспроизведения звука.
В разработанном алгоритме скорость воспроизведения звука считается величиной постоянной, а время вывода видеотекстуры на экран рассчитывается с учетом производительности (кадров в секунду) подсистемы визуализации. Для корректной синхронизации видео и звука необходимо, чтобы их воспроизведение началось одновременно. Для обеспечения синхронизации видео и звука после кэширования и запуска аудиопотока вводится задержка исполнения потока декодера (для преодоления задержки генерации звуковых буферов звуковой картой, которая составляет примерно 100–250 мс). После указанной задержки запускается процесс воспроизведения видео.
Еще одной серьезной проблемой при воспроизведении видео в подсистеме визуализации стало снижение производительности при воспроизведении видео разрешением 1280 на 720 и более. Причина снижения производительности – время загрузки одной видеотекстуры в память видеокарты, которое составляло от 15 до 20 мс.
Первоначально для загрузки видеотекстуры в подсистему визуализации применялась базовая функциональность обновления текстур. Она включает в себя получение указателя на содержимое существующей текстуры, копирование данных нового видеокадра в текстуру и помещение в видеопамять. Одним из предположений о причинах длительной загрузки текстуры являлось то, что видеокарте не хватает времени на обработку обновляемой в видеопамяти видеотекстуры, которая будет использована на следующем кадре подсистемы визуализации. С учетом указанного предположения был создан механизм заменяемых текстур.
Принцип данного механизма в том, что при запуске видео создавались две пустые текстуры, которые обновлялись бы заранее (за кадр или несколько кадров подсистемы визуализации до отображения), а при необходимости обновления видеотекстуры в подсистеме визуализации используемая текстура заменялась бы в материале трехмерного объекта. Замена используемой текстуры в материале занимает доли миллисекунды, однако применение данной системы не решило проблему, так как скорость обновления текстур продолжала составлять от 15 до 20 мс.
Следующим вариантом решения проблемы производительности могло стать обновление текстуры в другом потоке. Но использование дополнительного потока могло создать проблему, присущую многопоточности, – синхронизацию по- токов. Так, в одном варианте реализации при обновлении текстуры основной поток рендеринга ожидал бы окончания операции обновления, что сводило бы на нет эффективность данной системы. Поэтому механизм применения дополнительного потока был объединен с механизмом заменяемых текстур, когда одна текстура всегда содержит видеокадр, а вторая обновляется в дополнительном потоке. Однако и данный подход себя не оправдал, так как при использовании дополнительного потока текстура просто не отображалась в подсистеме визуализации, несмотря на то, что весь код ее обновления выполнялся правильно и ошибок не выдавал. Дело в том, что графический интерфейс OpenGL, на котором основан компонент подсистемы визуализации Horde3D, как и графический интерфейс DirectX, по своей специфике являются однопоточными, а потому команды, полученные из других потоков, игнорируются. При создании окна, в котором будет генерироваться изображение, создается так называемый контекст, который обеспечивает связь между созданным окном и графическим интерфейсом (DirectX, OpenGL). Контекст привязан к тому потоку, в котором создан, поэтому все команды графического интерфейса выполняются только в этом потоке. Соответственно, все команды подсистемы визуализации, связанные с обновлением или заменой данных в памяти видеокарты, могут выполняться только в том потоке, в котором создан контекст. Зачастую это основной поток программы, или так называемый поток рендеринга.
Существует возможность передачи управления контекстом другому потоку. Благодаря этому можно сделать этот поток способным управлять ресурсами видеокарты, однако все команды из основного потока будут игнорироваться. Теоретически есть возможность быстро переключить контекст между потоками для обновления текстуры в видеопамяти в дополнительном потоке, однако на практике операция по изменению родительского потока контекста довольно ресурсоемка. В результате проблема повышения производительности с помощью данного метода так и не решена.
Еще одним вариантом применения потоков и контекстов было создание второго контекста для дополнительного потока. В этом случае можно объединить ресурсы для двух контекстов, что позволит управлять ресурсами подсистемы визуализации из двух независимых потоков. Однако в большинстве случаев для использования второго контекста необходимо создание второго окна. Так как оконная подсистема GLFW не позволяет создавать более одного окна, а внедрение данной функциональности сложно и нет возможности проверить эффективность методики до ее внедрения, принято решение не использовать данную функциональность.
После попыток применения дополнительных потоков для обновления текстуры в подсистеме визуализации было решено вернуться к однопоточному обновлению текстур, но с использованием пиксельных буферов. Пиксельные буферы позволяют сократить время обновления текстур в видеопамяти за счет того, что центральный процессор необходим только для обновления информации в самом пиксельном буфере, а передача информации из пиксельного буфера в текстуру происходит с помощью видеокарты без участия центрального процессора. Однако даже с использованием пиксельных буферов скорость обновления текстур в видеопамяти продолжала колебаться в интервале от 10 до 20 мс.
Во время использования пиксельных буферов сделано одно важное наблюдение, которое впоследствии и позволило решить проблему производительности. Рассмотрим стандартный цикл работы графического приложения (рис. 3).
В большинстве случаев обработка и преобразование трехмерной сцены происходят до генерации кадра для того, чтобы в нем отображались последние состояния всех визуализируемых систем. Только графический интерфейс пользователя обрабатывается и генерируется после создания кадра, так как интерфейс должен накладываться уже на готовый кадр, иначе он будет затерт.
Во всех рассмотренных способах обновления видеотекстур в подсистеме визуализации обновление видео происходило до рендеринга сцены.
Обновление видео после рендеринга кадра позволило снизить время обновления текстуры в видеопамяти с 10–20 мс до 0,1–0,2 мс. Это дало возможность достичь стабильных 60 кадров в секунду, генерируемых подсистемой визуализации, даже при воспроизведении видео с разрешением 1920 на 1080 (FullHD).
Причин значительного снижения производительности может быть несколько. Первая: видеокарта не успевает обрабатывать поступающие команды. По сути процесс рендеринга изображения является конвейером. Процессор асинхронно задает команды видеокарте, которая сохраняет команды в очередь и последовательно их выполняет. Существуют определенные команды, которые могут вызвать синхронизацию процессора и видеокарты, то есть создавать ситуацию, когда одно устройство будет ожидать другое. Основными командами, способными вызвать синхронизацию, являются команды получения данных из видеопамяти и загрузки новых данных в видеопамять. Поскольку для получения данных из видеопамяти или загрузки новых данных видеокарте необходимо выполнить все стоящие в очереди задачи, центральный процессор ожидает, пока они будут выполнены. Соответственно, данная задержка ведет к снижению производительности подсистемы визуализации. При обновлении видео конвейер видеокарты мог не успевать выполнять операцию по смене кадров. Так как данная операция является асинхронной, а обработка команд внешних устройств и трансформация сцены могут занимать незначительное время (около одной миллисекунды), перед загрузкой новых данных в видеопамять видеокарте приходилось выполнять операцию по смене кадра. Вместе с тем после рендеринга кадра видеокарта в большинстве случаев оказывается свободной до операции смены кадра.
Второй причиной может быть использование многопоточности – в одном процессе становится больше потоков одного приоритета, следовательно, время, через которое процессу рендеринга будут отданы ресурсы процессора, может быть увеличено, а предоставляемое время, наоборот, уменьшено. Было замечено, что при отсутствии дополнительных потоков и использования стандартной функциональности обновления текстур подсистемы визуализации текстура размером 1920 на 1080 загружается в видеопамять за 5–8 мс, а также то, что задержка происходила не на этапе загрузки текстуры в видеопамять, а на этапе копирования нового видеокадра в пиксельный буфер – данный этап никак не связан с работой видеокарты и выполняется только центральным процессором. Это может означать то, что выполнение потока было приостановлено для выполнения других потоков с более высоким приоритетом. Данную проблему можно решить, если присвоить основному потоку программы (или потоку рендеринга, если поток рендеринга не является основным потоком программы), в котором происходит обновление пиксельных буферов, более высокий приоритет, чем у других потоков приложения.
В заключение можно сделать следующие выводы. Для воспроизведения видеоматериалов в подсистеме визуализации ТОС разработаны методы и алгоритмы воспроизведения видеоинформации в виртуальном трехмерном окружении; произведена оптимизация алгоритмов для достижения требуемого (не более 20 мс) времени генерации кадров в подсистеме визуализации при воспроизведении видеоинформации. Кроме того, разра- ботана подсистема визуализации ТОС, внедрен программный модуль для декодирования видео, позволяющий обрабатывать видеофайлы на различных программно-аппаратных платформах.
Литература
1. Mamrosenko K.A. Training simulation systems for open distance learning // Proceedings Conference Open Distance Learning Towards Building Sustainable Global Learning Communities. Hanoi, Vietnam: The Gioi, 2010.
2. Гиацинтов А.М., Мамросенко К.А. Описание архитектуры подсистемы визуализации тренажерно-обучающих систем // Гагаринские чтения: тр. Междунар. молодеж. науч. конф. М.: ИЦ МАТИ, 2011. С. 85–87.
3. OpenGL 2.1 Reference Pages. URL: http://www.opengl.org/sdk/docs/man/ (дата обращения: 18.05.2011).
4. Nicolas Schulz. Horde3D Documentation. URL: http://www.horde3d.org/docs/manual.html (дата обращения: 18.05.2011).
5. MPEG-2 video compression. URL: http://www.bbc.co.uk/ rd/pubs/papers/paper_14/paper_14.shtml (дата обращения: 18.05.2011).
6. YUV pixel formats. URL: http://www.fourcc.org/yuv.php (дата обращения: 18.05.2011).
7. NTUIT.ru: Курс: Common Intermediate ..: Лекция №11: Основы многозадачности. URL: http://www.intuit.ru/department/pl/cil/11/1.html (дата обращения: 18.05.2011).