Все началось с того что я купил себе новый монитор с поддержкой 3D ASUS VG23AH и в какой то момент мне захотелось воспользоваться возможностью посмотреть 3D фильм на нем. Подуглил бесплатные виде плееры для Mac OS, оказалось их не так много — всех чаще попадался плеер Bino. Скачал этот Bino, но посмотреть нормально так и не получилось, т.е. к плееру претензий нет, картинка была 3D, но видео страшно тормозило, картинка дергалась. Я воспользовался советом по повышению производительности к этой программе — там сказано — проверьте, как проигрывается этот фильм в VLC плеере, если уж и там будет тормозить, значит, мощностей вашего компьютера недостаточно/замените компьютер. В VLC все показывалось отлично без тормозов но, конечно, в моно режиме. Поскольку VLC — это opensource проект, я решил его немного подправить, чтоб он смог показывать стерео-фильмы, оказалось это сделать довольно просто.
Для начала я сделал fork этого репозетория и начал читать документы и howto. Сразу простроить проект у меня не получилось, были конфликты с установленным у меня mac ports, пришлось снести. Потом пришлось сделать up-merge для extras/tools/ragel/aapl компоненты, после этого все собралось по инструкции .
Дальше первое, что мне бросилось в глаза — это возможно добавлять в VLC — плагины (это такие модули, которые позволяют делать пост-обработку декодированных виде фреймов). Ну что-ж, я подумал, что этого будет достаточно для преобразования стерео изображения.
Отступление: исходный файл, который мне хотелось проиграть, имел в каждом видео фрейме 2 ракурса (слева картинка для левого глаза, справа — для правого) в общем, довольно стандартный формат для стерео-фильма.
Обработка сводилась к тому, что я формировал выходной видео буфер линия за линией из левого и правого полукадра соответственно. Ожидалось, что левый глаз будет видеть нечетные строчки, правый — четные.
Реализовать этот фильтр было просто (прочитал инструкцию взял за основу один из существующих). Но, конечно же, к успеху это не привело. На экране картинка выглядела полосатой, но линии не были синхронизированы с пикселями экрана, поэтому эффекта 3D в очках не наблюдалось.
Отступление: Хочу пояснить, как работает 3D режим на пассивных 3D мониторах. Я не специалист в этой технологии, поэтому буду описывать как дилетант.
В мониторах типа моего используется технология FPR (Film-type Patterned Retarder) для разделения ракурсов видео потока. Я ее представляю себе так, как будь то каждая линия пикселей на мониторе закрыта поляризационным фильтром, условно можно представить, что нечетные линии закрыты фильтром с вертикальной поляризацией, а четные с горизонтально, такие же фильтры стоят в очках, в результате один глаз видит только нечетные линии, второй четные.
Поняв это, я понял, что мне нужно делать пост-обработку непосредственно в рендере, чтоб я мог гарантировать попиксельно расположение выходного видео фрейма.
Посмотрел как реализован рендер в VLC для Mac. Оказалось все довольно просто. Каждый декодированный видео фрейм загружается в виде текстуры, которая в последствие отображается на экране средствами open GL. Преобразование графического формата из YUV в RBG происходит непосредственно в пиксельном шейдере. Это сразу натолкнуло меня на мысль — что нужно сделать. Поскольку известно, что пиксельный шейдер как раз формирует цвет для каждого пикселя экрана, мне остается только поправить этот шейдер, чтоб он при формирование цвета брал данные из нужной части текстуры, для четных и нечетных линий.
Вот пример кода (написано схематично, рабочий пример можно посмотреть тут ):
void main(void) {
vec4 x,y,z,result;
vec4 tc = TexCoord0;
+ float d = mod((floor(height*(tc.y+1.0))),2.0); //odd or even, height - is new uniform to get viewport height
+ if(d>0.1) tc.x += 0.5; //shift texture
x = texture2D(Texture0, tc.st);
y = texture2D(Texture1, tc.st);
z = texture2D(Texture2, tc.st);
result = x * Coefficient[0] + Coefficient[3]; //Coefficient - const coefficients for convert YUV to RGB
result = (y * Coefficient[1]) + result;
result = (z * Coefficient[2]) + result;
+ if(TexCoord0.x > 0.5) result = vec4(0,0,0,1); //cut off right side
gl_FragColor = result;
};
Если сравнивать с исходным кодом, то я добавил только строчки помеченные +.
После этого я уже получил правильное стерео изображение в левой части экрана.
В дальнейшем осталось только поправить размер картинки, и отрезать правую часть, и все — стерео-плеер готов.
Всем кому интересно — можно посмотреть код на github.
Фактически, чтоб получить стерео-плеер из VLC в общей сумме пришлось написать около 30 строк кода, это меня сильно поразило, и явилось причиной написания этой статьи.
PS: Уже потом, когда моя версия была сделана, я нашел отличный opensource стерео-плеер — sview.ru, на MacOS работает неплохо.
Автор: mangelov