Wojtek, czyli bot nagrywający wykłady

Ale po co?

Rozpoczął się październik, nowy rok akademicki, nowe przedmioty, nowe wykłady.

Na mojej uczelni wykłady są zazwyczaj nieobowiązkowe, równocześnie „góra” sugeruje robienie wykładów stacjonarnych, do tego często są one po prostu niepotrzebne na ćwiczeniach i do zdania egzaminu. Takie połączenie skutkuje niską frekwencją.

Na szczęście większość prowadzących zajęcia na moim kierunku poszła po rozum do głowy i zdecydowała się robić wykłady zdalnie.

Taka sytuacja pozwala na dużo wygodniejszą konsumpcję wiedzy co przyczynia się do zwiększenia zadowolenia studentów. A przynajmniej w teorii…

W praktyce i tak nie wszyscy uczestniczą w wykładach, bo liczą że ktoś je nagra i będą mogli sobie obejrzeć kiedy chcą i na przyspieszonym tempie. Problem zaczyna się gdy ten „ktoś” nie istnieje, bo nikomu się nie chciało włączyć nagrywania, bo „ktoś” kto zazwyczaj nagrywał zaspał itp...

Mem z Patrykiem wpychającym dużą dynię SpongeBobowi przez lejek; podpisane, że studenci przed sesją oglądający wykłady

Fajnie byłoby mieć kogoś, na kim można polegać, kogoś kto zawsze nagra i udostępni wykład… Powitajcie Wojtka - naszego nowego kolegę.

Czy to jest możliwe?

Przed wykonaniem projektu zawsze zadaję sobie to pytanie: Czy jestem w stanie w ogóle zrobić coś takiego?

Aby lepiej zaplanować sobie próbę stworzenia rozwiązania, problem rozbijam na kilka mniejszych etapów, w tym przypadku były to:

  • Uruchomić Chrome tak, aby kontrolować go z kodu
  • Wejść na spotkanie na Webexie - przejść przez proces wpisywania imienia oraz Captcha
  • Nagrać przeglądarkę - zarówno obraz jak i dźwięk
  • Umożliwić zatrzymanie nagrywania oraz wykryć zakończenie spotkania
  • Umożliwić wywoływanie nagrywania przez dowolną osobę na serwerze Discord
  • Zrobić to w miarę intuicyjne i odporne na błędy człowieka
  • Powtórzyć wszystko dla Microsoft Teams
  • Umożliwić łatwy dostęp do nagrań

Tak więc, rozpocząłem pracę, docelowo bot będzie uruchomiony gdzieś w chmurze. Aby umożliwić przenośność kodu skorzystałem z Dockera.

Docker to ciekawe i bardzo użyteczne narzędzie jednak na początku nie było mi przydatne. Ciężko pisze się kod, który dokonuje interakcji z graficzną stroną internetową jeżeli wszystko działa bez interfejsu użytkownika. Dlatego uruchamiałem mojego bota lokalnie na komputerze. Wykorzystałem bibliotekę Puppeteer w celu łatwego kontrolowania przeglądarki.

Zrzut ekranu wejścia na spotkanie Webexa
Zrzut ekranu wejścia na spotkanie Webexa

Teraz należy wejść na spotkanie. Webex oczekuje rozwiązania Captchy 😔 Początkowo myślałem, że to nie do obejścia, ale przecież mógłbym wysłać zdjęcie Captchy do przepisania komuś na Discordzie - ktoś mi odpisze i wejdę na spotkanie.

Używając komendy /nagraj mogę kazać Wojtkowi wejść na spotkanie, on po chwili odpisuje mi prośbą o rozwiązanie Captchy
Używając komendy /nagraj mogę kazać Wojtkowi wejść na spotkanie, on po chwili odpisuje mi prośbą o rozwiązanie Captchy

To działa!

Teraz potrzebuję włączyć nagrywanie. Bardzo szybko natrafiłem na bibliotekę puppeteer-screen-recorder. Działała ona praktycznie out of the box, ale nie nagrywa dźwięku. Po dłuższym googlowaniu dowiedziałem sie, że puppeteer nie ma możliwości przechwytywania dźwięku 😐

Nie wszystko stracone, mogę przecież uruchomić w dockerze serwer dźwięku PulseAudio z wirtualnym głośnikiem, który będę mógł osobno przechwycić używając programu ffmpeg.

Po skończonym wykładzie można zamknąć kartę z webexem, połączyć pliki z obrazem i dźwiękiem w jeden finalny mając nadzieje, że nic się nie rozjedzie 😅 A następnie wysłać wiadomość na kanał na Discordzie.

W taki sposób wypełniłem 5 kroków i stworzyłem MVP. Czas na testy w prawdziwym środowisku

Trochę walki z błędami

Oczywiście nigdy nie jest tak, że program działa bezbłędnie po napisaniu. Wojtek tak samo miał swoje problemy, nie raz potrafił się wywalić i potrzebował licznych poprawek.

Sama implementacja wchodzenia na spotkanie na Teamsach nie była problematyczna. Jednak pojawił się zupełnie niewyjaśniony błąd. Po około 5 sekundach nagrywania ekranu z Microsoft Teams ekran był kompletnie biały. Wiele nieudanych prób naprawy zmusiły mnie do porzucenia biblioteki nagrywającej na rzecz postawienia serwera X11. W tym celu wykorzystałem program Xvfb.

Tak więc obecnie Wojtek uruchamia Chrome w pełnoekranowym trybie w wirtualnym pulpicie z wirtualnym głośnikiem i nagrywa jak normalny ekran. Nie obyło się bez problemów podczas włączania PulseAudio i X11, dlatego profilaktycznie przed uruchomieniem muszę usuwać tymczasowe pliki jak /run/pulse* czy /tmp/.X1-lock, oraz killować procesy X11 i pulseaudio.

Jak już wszystko działało w miarę stabilnie, to Microsoft wypuścił aktualizację Teamsów znacznie zmieniającą interfejs co oczywiście wywróciło Wojtka 🙄

Jednym razem spadło z rowerka nagrywanie dźwięku, jednak obraz się nagrał, co zwróciło moją uwagę to bardzo mały rozmiar finalnego pliku. Okazało się, że niepoprawnie kompresuję audio i niepotrzebnie są w nim dwa kanały (stereo). Dzięki jednemu bugowi znalazłem kolejny🤗

Innym problemem, który nie był widoczny na testach było zatrzymanie nagrywania po około 4 minutach. Po dłuższym szukaniu przyczyny znalazłem: ffmpeg, którym nagrywam, stale wypisuje na ekran aktualne statystyki takie jak czas nagrania. Te dane wpisuje do bufora, ten bufor jest dostępny dla programu, który uruchomił ffmpega - Wojtka. Ale Wojtka nie obchodzi co tam sobie ffmpeg pisze, więc tego nie czyta. Po około 4 minutach cały bufor się zapycha i system wstrzymuje proces ffmpeg. Naprawienie tego to proste dodanie stdio: "ignore" do opcji przy uruchamianiu procesu 🤦‍♂️

O kompresji

Filmy dużo ważą, tak po prostu. Na szczęście pewni mądrzy ludzie wymyślili algorytmy kompresji, które można wykorzystać.

Priorytetem dla mnie jest łatwy dostęp filmu dlatego hostowane są na serwerze HTTP. To umożliwi oglądanie filmu bez konieczności pobierania całego. Format .mp4 jest szeroko wspierany w przeglądarkach internetowych co sprawia, że każde urządzenie jest w stanie taki film wyświetlić.

Ale czy na pewno? Format mp4 to jedynie kontener, to jak zapisany jest obraz w nim to sprawa innego kodeku. Naturalnie najpierw spróbowałem możliwie najlepszego kodeku - h255, jednak okazuje się, że nie jest on natywnie wspierany na wszystkich urządzeniach. System Windows nawet każe sobie zapłacić w 2022 roku za możliwość otwarcia takiego pliku 🤯

Dlatego zdecydowałem sie użyć h254, który oferuje gorszy współczynnik jakości do rozmiaru, ale lepszą kompatybilność.

A skoro o rozmiarach mowa. 90 minut wykładu waży… około 25 MB, przy czym wszystko na slajdach jest czytelne. Jeżeli wykładowca oprócz slajdów pokaże kamerkę lub coś innego co się rusza to rozmiar wzrasta o kilka MB.

Aby osiągnąć taki wynik używam takiej komendy:

# Nagrywanie dźwięku:
ffmpeg -f pulse -i auto_null.monitor -y $AUDIO_PATH
# Nagrywanie obrazu:
ffmpeg -f x11grab -framerate $FRAMERATE -r $FRAMERATE -video_size $WIDTHx$HEIGHT -i :1.0 -c:v libx264 -preset superfast -pix_fmt yuv420p-y $VIDEO_PATH
# Łączenie obu nagrań:
ffmpeg -r $FRAMERATE -i $VIDEO_PATH -i $AUDIO_PATH -c:v libx264 -crf $CRF -c:a aac -abr 1 -b:a $BITRATEk -ac 1 $FINAL_PATH -y

Ja używam parametrów: WIDTH=1280, HEIGHT=720, FRAMERATE=4, CRF=38 BITRATE=32.

Jak się sprawdza po pół roku?

Minął semestr obecności Wojtka z nami. Czy było warto? Absolutnie!

Jakieś statystyki? Ponad 60 wykładów w około 2GB nagrań. Najpilniejszy student kierunku.

Uważam, że to fajny projekt. Dużo się nauczyłem robiąc go, choć trochę się wkurzałem gdy nie działał jak chciałem. Jednak najważniejsze, że mogłem komuś innemu pomóc umożliwiając oglądnięcie wykładu później.

Dzięki za przeczytanie
Jakieś pytania, sugestie, opinie? Śmiało, napisz do mnie