My Shiny Weblog!

programming, photography and lifestyle

IPTV върху Media MVP

От извесно време насам работя по един IPTV проект. Или по-точно правя простичък mvpmc базиран IPTV плейър за Hauppauge MediaMVP. Накратко хардуерът е следния:

  • 250Mhz PowerPC 405
  • Hardware MPEG decoder
  • 16 Mb RAM
  • 10/100 Ethernet

Mvpmc проектът е линукс базиран, ориентиран е към използването на този хардуер за домашен audio/video център. Връзваш си телевизора към бокса и получаваш на него всичките си филми и музика от компютъра. Естествено може да тегли и разни неща от Интернет, както и да ползва web базирани услуги – примерно ReplayTV. Заедно с mvpmc идва и целия toolchain за компилиране на софтуер за PowerPC 405 архитектурата. В началото тръгнах с идеята, че мога да мофифицирам mvpmc интерфейса за моите нужди. А те са сравнително прости. Искам плейъра да може да прави MPEG-TS streaming по HTTP от предварително зададен плейлист. Също така ми трябва да работят основните копчета по дистанционното, както и някои елементарни OSD менюта. mvpmc поддържа тези неща, но се оказа изключително голям, като размер на кода и труден за модифициране. Целият е написан на C:

1
2
3
4
5
6
debian:~/mvpmc/src# cat *.c | wc -l
44847
debian:~/mvpmc/src# cat *.c | grep switch | wc -l
280
debian:~/mvpmc/src# cat *.c | grep case | wc -l
1347

Това са 44847 реда код с 280 switch-а и 1347 case-а (без библиотеките), в които ми омръзна да ровя още на първия ден. Може би нямаше да е проблем, ако всичко беше организирано модулно, така че човек да добави функционалност пипайки на сравнително малко места.

Първата задача беше да напиша някаква програма, която чете HTTP поток от зададен адрес. Понеже такива програми често ми трябват – вече имам написана такава с libevent. Тук се роди идеята да сложа libevent в тази линукс дистрибуция. Имах някакви съмнения, че това ще се получи. Главно заради малкото памет (16 Mb), в която трябваше да се побере всичко. Линукс ядрото е 2.4, което ознчава, че няма epoll. Понеже аз работя с малко сокети, това не е от значение. Libevent се справи и без него. Останах приятно изненадан колко добре се справя линукса с управлението на паметта. Библиотеката тръгна нормално и програмата четеше потока използвайки под 1 Mb памет.

Следващите дни прекарах в четене и разучаване на демуксерите и библиотеките, които управляват хардуера. И двете се оказаха сравнително малки и много добре написани. След прочитане на данни от MPEG-TS потока, те се записват в буфер и се предават към първата библиотека – libts_demux. Тя връща MPEG-PES поток, който се записва в друг буфер. Данните от този буфер се предават към libdemux, от където се получават няколко броя MPEG-ES потоци – един за audio, един за video и един за субтитри. Тези библиотеки са базирани на libdvbpsi, която се използва от VLC. За да работи добре libdemux се използва един 4 Mb кръгов буфер, който пълни значително малкото памет на машината. Цялата сложност на декодирането на mpeg потоците е в тези библиотеки. В началото си изгубих доста време да “сглобявам” разпарчетосани пакети в буферите, в последствие се оказа, че това не е нужно – библиотеката се грижи за тези неща. Естествено възпроизвеждането на тези потоци не може да стане софтуерно на този бавен процесор. За целта машината разполага с хардуерен декодер. Излизащите от демуксера данни трябва да бъдат записвани в /dev/adec и /dev/vdec. Тук не бях много сигурен как да постъпя. В mvpmc се ползват нишки с доста сложна синхронизация за изваждане на данните от демуксера и писането им в декодера. Аз реших отново да ползвам libevent, този път с timer callbacks. Най-напред тръгна звука. Дълго време се борих с видео потока и управлението на телевизора, след доста псуване и той тръгна. Необходима беше инициализация на SCART интерфейса, задаване на PAL резолюция и показване на OSD повърхност. Тук идва тънкия момента на синхронизацията между двата потока. Имам две callback функции, които се извикват през определено време. Едната пише в /dev/adec, другата в /dev/vdec. Хардуерът се грижи за синхронизация на потоците в рамките на неговата вътрешна памет. Обаче при подаване на разсинхронизиран поток се получава разсинхронизирана телевизия. Добре познатия ефект от някои кабеларки – думите на водещата идват преди да си мръдне устните. При фолк певиците това се случва дори и със синхронизиран поток (ясно защо). Изключително дразнещ момент. Този проблем го реших с налучкване на времето през което да се викат двете функции. Все още не съм сигурен дали няма да ми трябват някакви по-сложни JIT синхронизции. Тестовете ще покажат.

Следващият етап беше работата с дистанционното. Дистанционното се вижда в линукса като /dev/rawir. При натискане на копче, там се подава един байт. Стандартното решение – libevent. За да не се бъркат нещата с видеото обаче, пуснах нов тред, който да управлява дистанционното. При пускане на програмата записвам всички канали в една TAILQ структура. При движение нагоре и надолу с дистанционното убивам треда, който чете потока и пускам нов за новия канал.

В този си вид, софтуера работи прилично и е около 600 реда C код. Остава да напиша един куп подробности. Потребителите трябва да имат меню за избор на канали. От потока трябва да се взема информация за аспекта на картината и да се променя динамично на по-хубавите телевизори. В един момент ще му сложа и EPG поддръжка. За тези цели в момента разучавам как да работя с OSD библиотеката на mvpmc. Оказва се много досадна работа.

Това е един много добър и изпреварил времето си хардуер – явно за това още се произвежда. Отворената му архитектура позволява лесното писане на най-различни приложения за него.