Вступление
Привет хабралюдям!
В своей предыдущей статье я начал рассказывать о разработке плеера на основе VLC. В данной статье я расскажу об основных аспектах и сложностях, с которыми мне пришлось столкнуться при написании плеера. Вот как выглядит главное окно программы:
Главное окно программы
Отрисовка кадра на экран с помощью Direct3D
Итак, как я уже говорил для декодирования потокового видео используется библиотека LibVLC, которая является основной частью плеера VLC. Плагин VMem позволяет декодировать видео поток, применяя к нему различные фильтры и в конце концов сохранять ее в пользовательский участок памяти. Для того, чтобы ускорить вывод картинки я использовал Direct3D 9. В Direct3D есть понятие поверхностей(surfaces) а также текстур(textures). Поверхность — это массив байтов имеющий размер width x height:
Схематичное изображение поверхности
Т.е. по сути поверхность есть bitmap
Текстура отличается от поверхности тем, что ее можно «накладывать» на примитивы(полигоны). Также текстура имеет mip-map уровни — уменьшенные копии текстуры(применяется в 3D графике)
Чтобы вывести кадр фильм на экран, нам нужно создать внеэкранную поверхность, передать указатель на область памяти плагину VMem, затем скопировать поверхность в текстуру и вывести прямоугольник с натянутой на него текстурой. Схематично это можно представить так:
Процесс отрисовки
Промежуточное копирование поверхности в текстуру необходимо в силу особенности доступа к поверхностям и текстурам в Direct3D.
Числа в скобках при отрисовке — это текстурные координаты, они показывают каким образом текстура будет накладываться на примитив.
Проблема многопоточности и растягивания окна
При растягивании окна back buffer, используемый при отрисовке остается тех же самых размеров, и поэтому при ресайзе окна, изображение растягивается и размывается. Чтобы этого не происходило нужно пересоздавать буфер. В Direct3D это делается вызовом IDIrect3DDevice9::Reset. При этом обязательным условием для успешного пересоздания буфера является удаление всех ресурсов из видео памяти. А поскольку LibVLC работает в отдельном потоке и записывает данные в нашу видео-поверхность, то необходимо синхронизироваться мютексами ну или секциями(кому как удобнее).
Также нужно решить вопрос об отрисовке. Поскольку кроме отрисовки видео в приложении были элементы, которые нужно было анимировать с частотой > 25 кадров сек. Реализовать это можно так: элементы GUI при отрисовке вызывают принудительную перерисовку окна также как и видео поток. Либо окно постоянно перерисовывается при простое приложения(как это делается в играх). При первом способе поток, декодирующий видео должен посылать сообщения в главный поток о том, что приложение нуждается в перерисовке, что дополнительно нагружает очередь сообщений. При втором способе посылать сообщения не нужно — приложение перерисовывается всегда.
Пользовательский интерфейс
Все кнопки и прочие элементы управления рисуются посредством вывода полупрозрачных примитивов с натянутыми на них текстурами. Каждый элемент имеет ограничивающий прямоугольник, при нажатии в котором происходит вызов «прикрепеленного» слота с нужным действием.
Для канала передач использовалась технология «kinetic scrolling»:
Для прокрутки используется «kinetic scrolling»
Технология скроллинга довольно простая, тем более в интернете есть множество статей по реализации данного эффекта.
Больше сказать по поводу пользовательского интерфейса нечего, кроме того, что над некоторыми эффектами типа «затухающее» название телепередачи пришлось поломать голову.
Заключение
Как я ни старался, а изложение моих мыслей получилось довольно сумбурным — хотелось рассказать о многих вещах, но в тоже время не писать целый талмуд о том, как писать приложения на DirectX. Если есть вопросы или предложения — буду рад!
P.S.(правка:)изначально картинки были загружены на dropbox, что привело к блокировке аккаунта. Исходные файлы не сохранились, поэтому схемы я перерисовал.