Журнал LinuxFormat - перейти на главную

LXF130:Clutter

Материал из Linuxformat
Перейти к: навигация, поиск
Python Настоящие задачи, чтобы вы поупражнялись в кодировании

Содержание

Python: Потоки видео

Python
Python для профессионалов
Python + PyGame
Python + Web
Python + Clutter
Скомбинируйте мощь видео от GStreamer с графической изощренностью Clutter – получится то, что Ник Вейч называет «чистая работа».

Первичная задача Clutter’а – упрощение процесса создания приятных графических интерфейсов. Он применяет подход «выстрелилизабыл» ко множеству вещей – в частности, к анимации: задав последовательность, запустите ее, и пусть себе выполняется.

Однако самому Clutter со всем этим не справиться. Анимация в главном модуле превосходна, но в других областях есть изъяны. Начнем с того, что в Clutter есть всего несколько примитивных объектовактеров; поэтому к нему и прилагается расширение на базе Cairo (на следующем уроке мы рассмотрим его подробнее).

Другая библиотека, на которую опирается Clutter – это GStreamer, кудесник по части потоков медиаданных. Когда речь заходит о мультимедиа, GStreamer – одна из наиболее часто используемых библиотек, и неудивительно, что для нее есть модуль Python. Чтобы применять его в своих приложениях, нужно немного разобраться в инструментарии GStreamer, но здесь, как всегда, на это мало места. К счастью, он не сложен. Нам и не нужно ничего замысловатого – есть у нас URI видеофайла или какогонибудь другого источника, вот мы и хотим его воспроизвести.

LXF130 76 1.jpg Мы еще не написали ни строки кода, а в окне уже вовсю идет потоковое видео.

Сначала зададим обычную сцену [stage] в Clutter:

>>> import gst
>>> import clutter
>>> import cluttergst
>>> stage=clutter.Stage()
>>> stage.set_title(“Clutter_Streamer”)
>>> stage.set_size(320,290)
>>> stage.set_color(clutter.Color(255,255,255,255) )
>>> stage.show()
>>>

Если вы в курсе предыдущих эпизодов нашего учебника, вам это должно показаться знакомым. Для новичков скажем, что здесь импортируются самые важные модули (заметьте, что наряду с gst нам понадобится cluttergst), создается объект Stage (окно, на языке Clutter), ему дается заголовок, размер и цвет фона, а затем он отображается. Вы увидите белое окно. Если нет, проверьте синтаксис, или скопируйте файл listing с DVD.

Весь мир — сцена

Теперь начнется интересное. Объекты, отображаемые на сцене Clutter, называются актерами [actor]. Существует несколько типов актеров. Некоторые относятся к базовым, например, объекты «прямоугольник» и «текст», и живут в главном модуле Clutter, но есть и другие, специальные актеры из внешних библиотек.

Объект VideoTexture находится в модуле cluttergst, и он похож на актера rectangle. У него много свойств из тех, что мы исследовали ранее, но он особый, так как подцепляется к проигрывателю видео и использует его для обновления своей текстуры. Это одна из сильных сторон Clutter – задав актера или даже установив ему анимацию на выполнение, незачем возвращаться к нему для контроля: он продолжит работать и делать то, что ему велено.

Проигрыватель видео известен под названием playbin; это встраиваемый плейер. Вы задаете ему ресурс для воспроизведения, и он увязывает все остальное самостоятельно. Давайте так и поступим:

>>> vid=cluttergst.VideoTexture()
>>> playbin=vid.get_playbin()
>>> playbin.set_property(‘uri’,’mmsh://live.camstreams.com/cscamglobal16MSWMExt=.asf’)
>>>

Сначала мы задали актера текстуры видео, затем извлекли созданный объект playbin. Прежде чем воспроизводить чтолибо, укажем источник. Лучший способ сделать это – вызвать метод set_property объекта playbin, включающий свойство, которое мы хотим задать (URI), и то, что мы хотим ему присвоить. Здесь мы использовали webадрес видео в Интернете: камера отслеживает дорожное движение на площади Пикадилли в Лондоне и дает поток живого видео (в отличие от тех, что просто обновляют картинку каждые пять секунд). Вам сразу же захочется избежать головокружительного восторга от вида двух дюжин автобусов, бодающихся перед светофором, и использовать локальный файл. Это значит, что все в порядке.

URI может ссылаться на файл или вообще что угодно, распознаваемое как корректный источник. GStreamer определит, что это такое и что с этим делать, при наличии правильных модулей расширения. Если таковых нет, возникнет сообщение об ошибке, говорящее о невозможности воспроизвести поток. Возьмите что-нибудь попроще: например, файл видео, который вы уже успешно просмотрели в Totem.

Для URI файлов (или чего угодно) потребуется префикс подходящего протокола. Например, правильным URI для локального файла будет file:///home/evilnick/Videos/killmike.ogg. Затем создадим другой объект GStreamer, чтобы использовать его для управления ранее созданным объектом playbin:

>>> pipe=gst.Pipeline(“pipe”)
>>> pipe.add(playbin)
>>> stage.add(vid)

Создав объект pipe, мы подключили его к playbin, с помощью pipe’овского метода add(). pipe используется просто как контейнер для нашего плейера. Наконец, на сцену можно добавить объект текстуры видео – именно он обновляется при воспроизведении потока.

Теперь у нас есть белое окно с отображаемой в нем текстурой видео, но посмотреть его пока нельзя: ведь оно еще не включено.

LXF130 77 1.jpg Ух ты! Поискав, вы найдете немало любопытных web-камер. Или возьмите свои файлы.

>>> pipe.set_state(gst.STATE_PLAYING)
<enum GST_STATE_CHANGE_ASYNC of type GstStateChangeReturn>

Контейнер pipe используется для управления потоком. С помощью предопределенной в gst константы gst.STATE_PLAYING мы включили плейер. Если вы воспроизводите файл, то увидите изображение сразу же. Если видна только часть картинки, расширьте сцену. Мы все еще находимся в интерактивном режиме Python, так что поправить сцену можно методом stage.set_size.stage.set_size(640,480) Мы также можем поменять состояние плейера, сделав паузу:

>>> pipe.set_state(gst.STATE_PAUSED)
<enum GST_STATE_CHANGE_SUCCESS of type GstStateChangeReturn>
>>> pipe.set_state(gst.STATE_PLAYING)
<enum GST_STATE_CHANGE_ASYNC of type GstStateChangeReturn>

Заметьте, что поток видео действительно приостановился. Вы воображаете, что такое бывает только с файлом, но и с подачей от webкамеры будет то же самое.

Мой поток отказал

Если канал GStreamer’а закрывается с ошибкой, или, реже, просто ничего не делает – проблема, возможно, с кодеком. Недостающие в вашем дистрибутиве кодеки можно добавить, установив пакет под названием gstreamer_plugins_good или наподобие. Все прочие пакеты, видимо, «bad» или «ugly» (здесь тонкий отсыл к мировой культуре, если кто понял). В зависимости от вашего местожительства, вы сможете законно скачать и установить дополнительные модули, чтобы увидеть больше потоков.

Пошалим

Мы всегонавсего создали Clutter ’скую версию поточного проигрывателя – как Kaffeine, но с меньшим числом опций. Однако текстура нашего видео – это актер Clutter 'а, так что с ней можно проделывать всякие штуки.

>>> vid.set_size(100,90)
>>> vid.set_size(100,290)
>>> vid.set_size(320,50)

Даже когда мы поменяли его размер, или подвинули этого актера...

>>> vid.move_by(10,10)
>>> vid.move_by(10,10)

К видео можно применять почти все методы актеров. В прошлом выпуске мы детально рассматривали методы анимации, и они сработают и для видеотекстуры. Кратко вспомним, что этот метод берет значение для эффекта анимации (константа Clutter, нумерующая различные стили), длительность в миллисекундах и список пар ключ/значение для свойств. Актер преобразует их в абсолютные величины. Например:

>>> vid.animate(clutter.LINEAR, 1000, ‘width’,640, ‘height’, 480, ‘x’,0,’y’,0)
<clutter.Animation object at 0x96f3884 (ClutterAnimation at 0xb4799a00)>
>>> vid.animate(clutter.LINEAR, 1000, ‘width’,320, ‘height’, 240, ‘x’,160,’y’,120)
<clutter.Animation object at 0x96f4284 (ClutterAnimation at 0xb4799c00)>

Чтобы поглубже вникнуть в анимационные преобразования, загляните в прошлый номер. Теперь для нашего маленького эксперимента с камерой у нас есть забава типа картинка-в-картинку; но необходимо разобраться еще с одним элементом. Во-первых, создадим новый видеообъект, такой же, как первый, но с другим потоком:

>>> vid2=cluttergst.VideoTexture()
>>> playbin2=vid2.get_playbin()
>>> playbin2.set_property(‘uri’,’mmsh://live.camstreams.com/cscamglobal5MSWMExt=.asf’)
>>> pipe2=gst.Pipeline(“pipe2”)
>>> pipe2.add(playbin2)
>>> stage.add(vid2)
>>> pipe2.set_state(gst.STATE_PLAYING)
>>> vid.set_position(0,0)
>>> vid.set_size(100,80)
>>> vid.set_depth(2)

С этим мы уже сталкивались ранее, а вот последняя строка появилась впервые. Концепция глубины (depth) – одно из 2.5 измерений Clutter. Объекты становятся ближе или дальше от сцены. По некой извращенной (по крайней мере, для меня) логике, для удаления от сцены глубину нужно брать положительной, а для приближения – отрицательной. Итак, если вам нужно наслоить объекты, как мы сделали здесь, возьмите глубину верхнего объекта большей, чем глубина нижнего.

Это меняет способ визуализации актеров, согласно настройкам перспективы, но для малых значений глубины вы ничего и не заметите. Впрочем, поиграйте с параметрами.

Теперь у нас есть все, чтобы создать многопотоковый браузер видео. В следующем куске кода будет создано настоящее приложение Python, так что запустите свой любимый текстовый редактор и введите следующее (или же найдите этот текст на LXFDVD): тор и введите следующее (или же найдите этот текст на LXFDVD):

LXF130 78 1.jpg Минуту назад мы созерцали Пикадилли, а теперь вы уже на Трафальгарской площади, или в Токио, или где-нибудь еще — был бы источник.

 import clutter
 import gst
 import cluttergst
 class videobrowser:
     def __init__ (self):
       self.channel1=”mmsh://live.camstreams.com/cscamglobal16?MSWMExt=.asfself.channel2=”mmsh://live.camstreams.com/cscamglobal5?MSWMExt=.asf# Инициа лизируем сцену
       self.stage = clutter.Stage()
       self.stage.set_color(clutter.Color(255, 255, 255, 255))
       self.stage.set_size(640, 480)
       self.stage.set_title(‘LXF Traffic Watch ­ press “t” to toggle view’)
        self.stage.connect(‘key­press­event’, self.parseKeyPress)
        self.stage.connect(“destroy”, clutter.main_quit)
        # Вторая видеотексту ра
        self.video1 = cluttergst.VideoTexture()
        self.playbin1 = self.video1.get_playbin()
        self.playbin1.set_property(‘uri’, self.channel1)
        self.pipeline1 = gst.Pipeline(“pipe1”)
        self.pipeline1.add(self.playbin1)
        self.video1.set_position(0,0)
        self.video1.set_size(640, 480)
        self.video1.set_depth(­2)
        self.stage.add(self.video1)
        self.pipeline1.set_state(gst.STATE_PLAYING)
        # пошел второй
        self.video2 = cluttergst.VideoTexture()
        self.playbin2 = self.video2.get_playbin()
        self.playbin2.set_property(‘uri’, self.channel2)
        self.pipeline2 = gst.Pipeline(“pipe2”)
        self.pipeline2.add(self.playbin2)
        self.video2.set_position(0,0)
        self.video2.animate(clutter.LINEAR, 1000, ‘rotation_angle_y’, 0, ‘rotation_angle_z’, 0, ‘width’, 80, ‘height’, 60 )
        self.stage.add(self.video2)
        self.video2.set_depth(0)
        self.pipeline2.set_state(gst.STATE_PLAYING)
        # вывес ти сцену и начать работу
        self.stage.show_all()
        clutter.main()
      def parseKeyPress(self, stage, event):
        print ‘parsekey got ‘, self, event
        # обработаем на жатие на клавишу
        if event.keyval == clutter.keysyms.q:
           # q значит вы ход
           clutter.main_quit()
        elif event.keyval == clutter.keysyms.t:
           # на жатие на t меняет ролики мес тами
           if self.video1.get_depth() == ­2 :
              #video2 на ходится сверху
              self.video2.animate(clutter.LINEAR, 300, ‘rotation_angle_y’, 360, ‘rotation_angle_z’, 360, ‘width’, 640, ‘height’, 480 )
              self.video1.animate(clutter.LINEAR, 1000, ‘rotation_angle_y’, 0, ‘rotation_angle_z’, 0, ‘width’, 80, ‘height’, 60 )
              self.video2.set_depth(­2)
              self.video1.set_depth(0)
           else :
              # video 1 на ходится сверху
              self.video1.animate(clutter.LINEAR 300, ‘rotation_angle_y’, 360, ‘rotation_angle_z’, 360, ‘width’, 640, ‘height’, 480 )
              self.video2.animate(clutter.LINEAR, 1000, ‘rotation_angle_y’, 0, ‘rotation_angle_z’, 0, ‘width’, 80, ‘height’, 60 )
              self.video1.set_depth(­2)
              self.video2.set_depth(0)
   if __name__ == ‘__main__’:
      videobrowser()
Скорая помощь

К некоторому недоумению, метод get_uri() у актера VideoTexture ничего не возвращает – ведь это просто текстура; а вот в объекте playbin содержатся данные по URI, и если вы забыли, с чем связан поток, воспользуйтесь playbin.get_property(‘uri’).

Код init создает два видеопотока, размещая один как актера над тем, что больше, а другой помещая в угол. Через несколько секунд потоки оживут, и вы увидите двухэтажные автобусы и прочее. Хитрая часть кода – перехват нажатий клавиш. Если фокус находится в окне, любое нажатие клавиши вызовет событие. Метод stage.connect() связывает этот сигнал с методом parseKeyPress(), определенным в главном классе приложения. Нажатие клавиши t поменяет два видео местами. Нужно помнить о том, что глубины следует задать заново, чтобы новая, маленькая картинка переместилась вверх (по значению параметра depth определяется, какой актер находился сверху при нажатии кнопки).

Не бойтесь экспериментировать, добавлять экраны и играть с вызовами анимации. Clutter выполнит ее за вас, и о действиях после остановки волноваться нет нужды. Клавиша Q привязана к чистому выходу из приложения.

Идем дальше

Можно добавлять другие потоки и располагать их каскадом один над другим. Вместо переключения двух потоков, их можно вращать вокруг одной точки. Также можно поиграть с анимацией: значения, задаваемые в методе animate, абсолютны, а не относительны, поэтому угловые повороты кратны 360 градусам – чтобы видеотекстура поворачивалась правильно.

В прошлых двух частях нашего сериала мы рассматривали актеров clutter.Text(). Добавить текстового актера, чтобы вы могли видеть, какой поток воспроизводится, или поменять текст при смене экрана, опять-таки не заботясь об анимации, довольно просто. Для потоков аудио, думаю, вам захочется приглушить звук при сворачивании окна.

Еще больше штучек с объектами GStreamer мож но отыскать в документации GStreamer: на данный момент она обширнее документации по PyClutter. Или взгляните на учебник GStreamer в LXF125.

Ах, этот Flash!

Некогда любой вид живого видеопотока был простым потоковым сокетом: присосись да пользуйся. Сейчас, кажется, они норовят требовать эти дурацкие плейеры, осно ванные на Flash. Уж не знаю, зачем: то ли для статистики пользователей, то ли для «защиты» контента… однако наряду с усложнением открытия в Firefox нескольких потоков одновременно, это также затрудняет поиск подходящих живых потоков. Хорошая добыча – камеры, отслеживающие дорожное движение; а если вам хочется пошалить, знайте, что некоторые встроенные Flash-плейеры любезно показывают URL сырого потока в исходном тексте страницы. Но я вам этого не говорил. Т-с-с!

Слово на ушко

Объекты GStreamer воспроизводят звук любого видеоресурса автоматически, и если вы просматриваете два источника одновременно, то услышите только какофонию. Для изменения звукового вывода (и, при желании, его перенаправления) пригодятся функции gst, а для простой регулировки громкости у актера VideoTexture из Clutter есть подходящий метод set_audio_volume().

Персональные инструменты
купить
подписаться
Яндекс.Метрика