- PVSM.RU - https://www.pvsm.ru -

Массовая запись с камер на выборах

Внимание. Хабр — не для политики. Пожалуйста, воздержитесь от обсуждения оной в комментариях.
В предверии первых выборов в России, в ходе которых все участки были оснащены веб-камерами, многие выражали желание записать для себя видео с камеры. Для этого предлагались разнообразные решения, от записи FRAPSом, до использования ffmpeg и так далее. Самым удачным, на мой взгляд, была утилита Qwertovsky [1], выложенная тут [2]
В данном небольшом топике я хотел бы предложить свое решение и вкратце напомнить, как вообще работает вся эта система, благо что завтра состоятся парламентские выборы на братской Украине, за ходом которых любой желающий может наблюдать на сайте vybory2012.gov.ua [3].


Итак, что следует знать:
Для каждой камеры раз в 15 секунд генерируется свежий плейлист, в котором содержатся прямые ссылки на последние 4 кусочка видео, длительность каждого кусочка — 15 секунд. Соответственно, раз в минуту плейлист полностью обновляется, а чанки продолжают быть доступны еще некоторое время.
Плейлист дублируется на нескольких серверах, доступен по ссылке вида http://server/variant.m3u8?cid=uid&var=orig и выглядит он примерно так:

#EXTM3U
#EXT-X-MEDIA-SEQUENCE:6885
#EXT-X-TARGETDURATION:15
#EXT-X-ALLOW-CACHE:NO
#EXT-X-PROGRAM-DATE-TIME:2012-10-27T11:02:08Z
#EXTINF:13,
/segment.ts?cid=f0ffd596-aaa6-4601-9432-70d717dd666a&var=orig&ts=1351335728.24-1351335741.20
#EXTINF:11,
/segment.ts?cid=f0ffd596-aaa6-4601-9432-70d717dd666a&var=orig&ts=1351335741.20-1351335752.28
#EXTINF:11,
/segment.ts?cid=f0ffd596-aaa6-4601-9432-70d717dd666a&var=orig&ts=1351335752.28-1351335763.40
#EXTINF:14,
/segment.ts?cid=f0ffd596-aaa6-4601-9432-70d717dd666a&var=orig&ts=1351335763.40-1351335776.92
#EXTINF:11,
/segment.ts?cid=f0ffd596-aaa6-4601-9432-70d717dd666a&var=orig&ts=1351335776.92-1351335788.36

Ссылка вида /segment.ts?cid=f0ffd596-aaa6-4601-9432-70d717dd666a&var=orig&ts=1351335728.24-1351335741.20 в данном случае обозначает кусочек видео с камеры f0ffd596-aaa6-4601-9432-70d717dd666a и интервалом с 1351335728.24 по 1351335741.20 в несколько усложненном формате Unix time.
Периодическое, раз в минуту, распарсивание плейлиста и скачивание всех доступных кусочков даст нам в результате максимум информации с камеры, за исключением тех моментов, когда на стороне камеры будет исчезать доступ в Интернет. Например, как-нибудь так:

# -*- coding: utf-8 -*-
# vybory2012 (Proof of concept), yegorov-p
import urllib
import os
from time import strftime, localtime, sleep
import socket
import threading

#Адрес сервера syslog, куда сыпалась вся статистика
syslog_server='127.0.0.1'
syslog_port=514
#Корневая папка, куда будем складировать чанки
directory='dumps'

#Массив с id камер, их хешами и списком серверов, откуда можно тянуть чанки
cams=[
['563-1', "f0ffd596-aaa6-4601-9432-70d717dd666a",["82.207.0.3","82.207.0.3","82.207.0.3"]]
]

#обозначения уровней для сислога
LEVEL = {
	'emerg': 0, 'alert':1, 'crit': 2, 'err': 3,
	'warning': 4, 'notice': 5, 'info': 6, 'debug': 7
}

#процедурка, пишущая в сислог. Копирайты потеряны, сорри =(
def syslog(message, level=LEVEL['notice'], host=syslog_server, port=syslog_port):
	sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
	data = '<%d>%s' % (level + 24, message)
	sock.sendto(data, (host, port))
	sock.close()

#Основная процедура
def cam_rip(num,hash,servers):
    syslog('Recording cam %s at %s'%(hash, num), level=LEVEL['info'])
    try:
        os.mkdir('./%s/%s'%(directory,num))
    except:
        pass

    #Запускаем бесконечный цикл
    while 1:
        try:
            server=servers[0]
            #Скачиваем текущий плейлист
            page = urllib.urlopen("http://%s/variant.m3u8?cid=%s&var=orig"%(server,hash)).read()
            #Если плейлист кошерен, выдираем ссылки на отдельные чанки и скачиваем их
            if '/segment' in page:
                for i in page.split('n'):
                    if '/segment' in i:
                        filename=strftime("%d-%b-%H-%M-%S", localtime(int(i[-13:-3])))
                        f=open('./%s/%s/%s.ts'%(directory,num,filename),'wb')
                        #syslog('Chunk %s saved'%(filename), level=LEVEL['notice'])
                        f.write(urllib.urlopen("http://%s%s"%(server,i)).read())
                        f.close()
            else:
            #В противном случае ругаемся в сислог и делаем ротацию сервера
                syslog('No signal!Rotating server on cam %s at %s'%(hash,num), level=LEVEL['err'])
                servers.append(servers[0])
                del servers[0]
            sleep(60)
        except Exception,e:
            syslog('Error on cam %s: %s'%(hash,e), level=LEVEL['err'])
            servers.append(servers[0])
            del servers[0]

try:
    os.mkdir(directory)
except:
    pass

#Запускаем кучу потоков для каждой из камер
for i in cams:
    threading.Thread(target=cam_rip, kwargs={"num": i[0],"hash": i[1],"servers": i[2]}).start()
    sleep(1)
syslog('System started.', level=LEVEL['notice'])

Запускаем данный скрипт, указав ему папку для складирования чанков и, опционально, адрес сервера сислога, куда будет сыпаться всяческая текущая информация.
Однако, мы обошли вниманием один момент — откуда взять исходные данные для плейлистов, а именно сервер и id камеры? Здесь стоит сделать небольшое отступление. Данная система успешно применялась на выборах в России, в частности, я при помощи вышеупомянутого питонового скрипта смог записать все камеры своего города. Однако, с тех пор произошли некоторые изменения в движке. Раньше все необходимые данные можно было получить даже не логинясь. Теперь же требуется сначала добавить камеру себе в избранное, после чего по адресу /account/channels?station_id=cid (где cid — id камеры) станет доступен файлик с хешами камер и ip-адресами серверов. Где-то в районе двух часов дня я решил собрать полную базу хешей и выложить ее в паблик, но сервера уже сейчас, еще до основной нагрузке периодически вываливают 502 ошибку, что осложняет процесс =) Из 32183 участков хеши доступны примерно с 5000, и понемногу эта цифра увеличивается.
В принципе, текущие данные можно получить, открыв нужную камеру и выполнив CorePlayer.instances.core_player_1.source.origin в Firebug или его эквивалентах, но так получится получить только текущий сервер (а он будет отваливаться, судя по опыту российских выборов).

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

Автор: PEgorov


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/python/18176

Ссылки в тексте:

[1] Qwertovsky: http://habrahabr.ru/users/qwertovsky/

[2] тут: http://habrahabr.ru/post/139292/

[3] vybory2012.gov.ua: http://vybory2012.gov.ua