Пользователь
0,0
рейтинг
5 сентября 2013 в 00:09

Разработка → Массовая запись с камер на выборах — 2

Хабр — не для политики. В данной статье рассматриваются исключительно технические аспекты реализации конкретного программного решения. Для всеобщего блага просьба отказаться от каких-либо политических дебатов, выступлений, агитации и тому подобных действий в комментариях. Кроме того, просьба не применять полученные знания в деструктивных целях, не начинать бекапить весь видеоархив без особой надобности и так далее. Спасибо.

8 сентября, единый день голосования. В этом году широкой общественности предлагается наблюдать через интернет за выборами столичного мэра. Ряд граждан считают для себя интересным записать картинку с камер: у кого-то политически мотивированный интерес, а большинству просто банально любопытно посмотреть на себя и знакомых глазами Интернета. Данная статья призвана продемонстировать принципы работы текущей системы и предложить работающие концепты.


Со времен прошлых выборов система немножечко изменилась (иначе и статьи бы не было), поэтому сначала вспомним, как всё работало раньше и как стало работать сейчас. Итак, у каждой камеры есть уникальный uid и пул серверов, с которых сыпется видео. Сформировав с использованием этих данных особый запрос, можно получить ссылку на кусочек видео, записанного выбранной камерой.

Для начала найдем данные обо всех существующих камерах. Мне показался наиболее простым следующий способ: начнем поиск по номеру участка, с 1 до 3800. Для этого отправим GET vybory.mos.ru/json/id_searchaaa/bbb.json, где bbb это uid, а aaa это len(bbb). Например, vybory.mos.ru/json/id_search/1/3.json

Получим json с информацией об этом участке, что-то вроде вот этого:
[{"id":7933,"name":"Участок избирательной комиссии №3","num":"3","location_id":1162,"address":"Новый Арбат, 36/9","raw_address":"г.Москва, Новый Арбат ул., дом 36/9","is_standalone":false,"size":null,"location":{"id":1162,"address":"Россия, Москва, улица Новый Арбат, 36/9","raw_address":"г.Москва, Новый Арбат ул., дом 36/9","district_id":1,"area_id":null,"sub_area_id":null,"locality_id":1,"street_id":1590,"lat":55.753266,"lon":37.577301,"max_zoom":17}}]


Особый интерес здесь представляет id. Отправим GET вида vybory.mos.ru/account/channels?station_id=id, в данном случае vybory.mos.ru/account/channels?station_id=7933

В ответе получим строчку с кракозяблами, на которые ругается мой редактор, но содержащие внутри хеши камер и адреса серверов. Выдерем оттуда хеши регуляркой вида
\$([0-9a-h]{8}-[0-9a-h]{4}-[0-9a-h]{4}-[0-9a-h]{4}-[0-9a-h]{12}) и ip адреса регуляркой вида .*?(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})

В результате получим требуемую информацию о камерах текущего участка:
2e9dd8dc-edd4-11e2-9a6b-f0def1c0f84c 188.254.112.2 188.254.112.3 188.254.112.4
2ea32990-edd4-11e2-9a6b-f0def1c0f84c 188.254.112.2 188.254.112.3 188.254.112.4

Далее начинаются ньюансы. Существует три типа камер: старые, новые и отсутствующие. Чем они отличаются я расскажу чуть позже, сначала разберемся, как их различать, а различать их очень просто — нужно отправить GET вида http://SERVER/master.m3u8?cid=UID
Новая камера вернет нечто вроде

#EXTM3U
#EXT-X-VERSION:2
#EXT-X-STREAM-INF:PROGRAM-ID=777,BANDWIDTH=3145728
/variant.m3u8?cid=e1164950-0c19-11e3-803b-00163ebf8df9&var=orig


Старая камера вернет что-то такого вида:
#EXTM3U
#EXT-X-MEDIA-SEQUENCE:136
#EXT-X-TARGETDURATION:15
#EXT-X-ALLOW-CACHE:NO
#EXT-X-PROGRAM-DATE-TIME:2013-09-04T12:05:40Z
#EXTINF:15,
/segment.ts?cid=2ea32990-edd4-11e2-9a6b-f0def1c0f84c&var=orig&ts=1378296340.93-1378296355.93
#EXTINF:15,
/segment.ts?cid=2ea32990-edd4-11e2-9a6b-f0def1c0f84c&var=orig&ts=1378296355.93-1378296370.93
#EXTINF:15,
/segment.ts?cid=2ea32990-edd4-11e2-9a6b-f0def1c0f84c&var=orig&ts=1378296370.93-1378296385.93
#EXTINF:15,
/segment.ts?cid=2ea32990-edd4-11e2-9a6b-f0def1c0f84c&var=orig&ts=1378296385.93-1378296400.93


Отсутствующая камера не вернет ничего, кроме 404 CID Was Not Found :)

Теперь, когда мы умеем получать информацию о камерах конкретного участка, напишем многопоточную парсилку, которая соберет нам всю необходимую информацию. Я предпочитаю складывать данные в бесплатный монголаб, но вполне можно обойтись и обычным shelve. Зная, что участков в москве 3500+, пробежимся циклом от 1 до 3800. Ниже набросанный на коленке, но тем не менее, работающий код. В нем, разумеется, нужно вписать своё печенько и пароли от сервера монги.

# -*- coding: utf-8 -*-
import json, re
import httplib
import threading
from time import sleep
import Queue
from pymongo import MongoClient

client = MongoClient('mongodb://admin:кусь@кусь.mongolab.com:43368/elections')

db = client['elections']
data = db['data']

data.drop()

def get_data(uid):
    print uid
    headers = {'Origin': 'vybory.mos.ru',
    'X-Requested-With': 'XMLHttpRequest',
    'User-Agent': 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0);',
    'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
    'Accept': '*/*',
    'Referer': 'http://vybory.mos.ru/',
    'Accept-Encoding': 'deflate,sdch',
    'Accept-Language': 'ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4',
    'Accept-Charset': 'windows-1251,utf-8;q=0.7,*;q=0.3',
    'Cookie': 'rack.session=кусь'
    }

    try:
        conn = httplib.HTTPConnection('vybory.mos.ru')
        conn.request('GET', '/json/id_search/%d/%d.json'%(len(str(uid)), uid), None,headers)
        resp = conn.getresponse()
        try:
            content = json.loads(resp.read())[0]
            conn.request('GET', '/account/channels?station_id=%s'%content['id'], None,headers)
            resp = conn.getresponse()
            cont = resp.read()

            cnt=0
            for i in cont.split('\x00')[1:]:
                cnt+=1
                uid=re.findall(r'\$([0-9a-h]{8}-[0-9a-h]{4}-[0-9a-h]{4}-[0-9a-h]{4}-[0-9a-h]{12})', i)[0]
                ip=re.findall(r'.*?(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})', i)

                conn2 = httplib.HTTPConnection('%s'%ip[0])
                conn2.request('GET', '/master.m3u8?cid=%s'%(uid), None,headers)
                info = conn2.getresponse().read()
                conn2.close()
                if '/segment.ts' in info:
                    camtype='old'
                elif '/variant.m3u8' in info:
                    camtype='new'
                else:
                    camtype='nil'

                #print content
                data.insert({
                            'name':content['name'],
                            'num':content['num'],
                            'addr':content['address'],
                            'uid':uid,
                            'ip':ip,
                            'cnt':str(cnt),
                            'type':camtype
                            })

        except Exception,e:
            pass

    except Exception,e:
        print e
    conn.close()


queue = Queue.Queue()
def repeat():
    while True:
        try:
            item = queue.get_nowait()
        except Queue.Empty:
            break
        get_data(item)
        sleep(0.01)
        queue.task_done()

for i in xrange(1, 3800):
    queue.put(i)

for i in xrange(10):
    t = threading.Thread(target=repeat)
    t.start()
queue.join()

print data.find().count(),'all cams'
print data.find({'type':'nil'}).count(),'offline cams'
print data.find({'type':'old'}).count(),'old cams'
print data.find({'type':'new'}).count(),'new cams'


Теперь у нас есть полностью собранная база камер. На момент написания статьи старых камер было 544, с ними, увы, получится работать только по-старому.
Но теперь у нас есть и 5778 новых камер, и у них есть одна особенность. Чанки со старых камер спустя очень короткое время протухают — нужно постоянно скачивать свежий плейлист, выдирать оттуда линки на чанки и качать их, пока не протухли. Новые камеры лишены этого недостатка. Можно качать чанки произвольных размеров за произвольный период времени, отправив GET вида http://SERVER/segment.ts?cid=UID&var=orig&ts=BEGIN-END Между BEGIN и END может быть не 15 секунд, а гораздо больше. Я остановился на чанках длительностью 5 минут. На самом деле, можно указать хоть час, но в некоторых случаях, насколько я могу судить, если трансляция прерывалась в течение пределов чанка, не скачается весь чанк. Грубо говоря, если вы пытаетесь скачать 8 часов из архива чанками по часу и при этом в течение нескольких минут одного чанка трансляции фактически не было, не скачается весь часовой чанк. Поэтому разумно выбрать чанк поменьше. Гуру алгоритмизации (которых, как мы помним, 10%) могут написать свой бинарный поиск, дабы не пропало ни секунды видео =)
Кстати, дабы закрыть вопрос — отсутствующей называется камера, которая зарегистрирована в портале, но по факту не работает.

Автоматизируем процесс скачивания. Здесь можно было сгородить свой многопоточный велосипед на питоне, но я решил воспользоваться сторонним софтом. Мы будем генерить метафайл со ссылками на чанки для aria2c, метафайлы для tsmuxer и последовательно их запускать.

Например, вот как-то так:
# -*- coding: utf-8 -*-
from time import sleep, time
from pymongo import MongoClient
import os
import subprocess
import shutil


#Корневая папка, куда будем складировать чанки
directory='e:/dumps'
#Размер чанка
delta=300
#Номер избирательного участка
num='666'


client = MongoClient('mongodb://кусь:кусь@кусь.mongolab.com:43368/elections')
db = client['elections']
data = db['data']


#Качать видео за последние 8 часов
start=int(time())-3600*8

#Создаем папку для дампов с избирательного участка
try:
    os.mkdir('%s/%s'%(directory,num))
except:
    pass

#Лезем в базу и достаем оттуда информацию о камерах с участка
for i in data.find({'num':num}):
    if i['type']=='nil':
        print 'Offline camera',i['uid']
    elif i['type']=='old':
        print 'Old camera',i['uid']
    else:
        print 'New camera',i['uid']
        f=open('links-%s-%s.txt'%(num, i['cnt']),'w')
        #Создаем поддиректории для каждой камеры
        try:
            os.mkdir('%s/%s/%s'%(directory,num,i['cnt']))
        except:
            pass

        cur=start
        files=''

        #Генерируем ссылки на чанки выбранной длины
        while True:
            if cur+delta>time():
                for ip in i['ip']:
                    url = 'http://{0}/segment.ts?cid={1}&var=orig&ts={2}.00-{3}'.format(ip,
                                                                                   i['uid'],
                                                                                   cur, time())
                    f.write('%s\t'%url)
                f.write('\n dir={0}/{1}/{2}\n out={3}.ts\n'.format(directory,num,i['cnt'],url[-27:]))
                files += '"{0}/{1}/{2}/{3}.ts"+'.format(directory,num,i['cnt'],url[-27:])
                break
            else:
                for ip in i['ip']:
                    url = 'http://{0}/segment.ts?cid={1}&var=orig&ts={2}.00-{3}.00'.format(ip,
                                                                                   i['uid'],
                                                                                   cur, cur+delta)
                    f.write('%s\t'%url)
                f.write('\n dir={0}/{1}/{2}\n out={3}.ts\n'.format(directory,num,i['cnt'],url[-27:]))

                files += '"{0}/{1}/{2}/{3}.ts"+'.format(directory,num,i['cnt'],url[-27:])

            cur+=delta

        #Генерируем метафайл для склеивания чанков в один большой файл.
        m=open('%s-%s.meta'%(num,i['cnt']),'w')
        m.write('MUXOPT --no-pcr-on-video-pid --new-audio-pes --vbr  --vbv-len=500\n')
        m.write('V_MPEG4/ISO/AVC, %s, fps=23.976, insertSEI, contSPS, track=3300\n'%files[:-1])
        m.write('A_AAC, %s, timeshift=-20ms, track=3301\n'%files[:-1])
        m.close()

        f.close()
        subprocess.Popen('aria2c.exe -i links-%s-%s.txt -d %s -x 16'%(num, i['cnt'], directory), shell=True).communicate()
        subprocess.Popen('tsMuxeR.exe %s-%s.meta %s/%s-%s.ts\n'%(num, i['cnt'], directory, num,i['cnt']), shell=True).communicate()
        shutil.rmtree('%s/%s'%(directory,num))
        os.remove('%s-%s.meta'%(num, i['cnt']))
        os.remove('links-%s-%s.txt'%(num, i['cnt']))


Опять же, код писался исключительно в целях проверки концепта и не является образцом соблюдения PEP8, но вполне работает. Скорость скачивания по понятным причинам зависит от многих факторов.

UPD Есть мнение, что старые камеры планомерно заменяют на новые. Вчера вечером было 337 старых и 5776 новых, сегодня утром — 273 старых, 5811 новых.

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

UPD Камеры постоянно меняют свой статус, обратите на это внимание. Со старой системой заменяются на новые.
Павел Егоров @PEgorov
карма
23,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (45)

  • +5
    Недавно занимался сегментирование видео для одного проекта, было интересно почитать про еще одну реализацию, и способы собрать видео из сегментов (обратная задача от моей). Спасибо!
  • +2
    Правильно ли я понимаю, что можно не писать весь поток, как в начале 2012-го (прошлый сентябрь пропустил, ничего не знаю), а в случае необходимости вытащить постфактум куски из архива? Или после окончания выборов лавочку прикроют?
    • НЛО прилетело и опубликовало эту надпись здесь
    • 0
      С новых камер, на данный момент — да, можно вытащить архив примерно со 2 сентября по текущее время. Какие там у организаторов планы, сказать не могу, но на украинских выборах картинка летела еще часов 6 после окончания выборов, так что в теории, если канал толстый, можно успеть выкачать все, что хочется.
  • +1
    Скажите, пожалуйста, меня интересует такой узкий аспект технической стороны как финансирование и монетизация проекта. Финансирование монтажных и прочих работ происходит заинтересованными политическими игроками или же предлагается размещать на сайте обычную рекламу? В обоих случаях, что надо согласовывать для того чтобы начать лепить свои камеры на участки и сделать свой сайт? Или достаточно договориться с какой-нибудь шишкой/родственником в правительстве чтобы он за откатик провел тендер и, о чудо, именно наш сайт выиграл госзаказ? Прошу не воспринимать вопросы в контексте политики, тут скорее вопрос себестоимости и жажды наживы. Представляете, если через пару лет все участки будут утыканы камерами? Думаю заинтересованные лица должны уже сейчас с этим как-то бороться!
    • +5
      Советы хозяйке: знаете ли вы, что эксперты оценивают установку веб-камер на всех избирательных участках страны в 15 миллиардов рублей, а кусок картонки, которым всего на три секунды закрывается объектив для вброса, не стоит вообще ни копейки? ©
  • 0
    Планируется ли полный бекап всех записей и подключение краудсорса для просмотра (можно совместить в день голосования)?
    • 0
      Не знаю — мной нет =) Отдельные организации, насколько я могу судить по их сайтам, вообще советуют аналогом фрапса сохранять. Штабу одного из кандидатов я инструкции эти показал до публикации на хабре, поскольку справедливо полагал, что в случае опубликования на хабре концепта, существует отличная от нуля вероятность огораживания со стороны организаторов видеонаблюдения. Поскольку с их стороны в течение суток никакой реакции не было, значит либо не планируют записывать, либо их это не особо интересует =)
      Я сомневаюсь, что это можно сделать без должно подготовки. 15 секунд видео весят около мегабайта. Примерный расчет показывает, что видео со всех камер за 24 часа займет примерно 38 терабайт. Отдельные камеры да, забекапить можно, но я лично не вижу в этом никакого последующего применения, мне так вообще больше технические подробности были интересны.
      • 0
        Краудсорсинг — наше всё. Например, я хочу писать или весь свой район (34 участка), или чуть меньше, если не потяну. Подозреваю, что найти в Москве сотню человек, которые тоже бы записали по району, задача вполне подъёмная.
        • 0
          Нет нужды делить по районам… Необходимо делить по размеру storage'а, bandwidth'у. Плюс прикрутить модифицированный LOIC и будет реальный Краудсорсинг.
      • 0
        Штаб одного из кандидатов вообще как будто не существует. Тоже писал пару раз и как будто в бездну. Понимаю, что дел дофига и без этого, но хоть признаки жизни подали бы.
      • 0
        38 терабайт в наше время смешная цифра, тут больше проблем с отсмотром материала. Проще сделать в день голосования пока есть интерес.
        • 0
          Вопрос не в отсмотре, а в том, чтобы иметь на руках материалы и проверять жалобы самостоятельно, а не на коленках ползать, чтобы выдали
  • +3
    Прошлые выборы были, если мне память не изменяет, 4го декабря? Так вот, 2го или 3го у меня родилась идея, поздновато, но всё же. Чтобы каждый человек с активной гражданской позицией взял себе по одному участку/камере и писал с нее на свой компьютер (для этого следовало бы создать скрипт/программу), и любой другой человек мог скачать интересующие записи с нужного участка (скачанное скриптом анонсируется на специально созданный torrent-трекер). В результате эдакого акта краудсорсинга у общественности была бы полная база видео с выборов.
    Хотя… Камер на выборах тысяч, вроде 100. Тут для того, чтобы поддержать петицию на roi, народу днем с огнем не сыщешь :-)
    • 0
      Вообще через госуслуги вроде можно было заказать любую запись.

      На прошлых выборах какие-то ребята писали систему, куда можно было залить видео с камер, оно ускорялось на тех моментах, когда к урне никто не подходил, а во всех прочих случаях нужно было смотреть, бросили ли бюллетень в урну, или просто кто-то мимо прошел. Если у Вас академический интерес, можете с webvybory2013.ru/ выбрать какой-нибудь небольшой участок, вон Гатчину, например, там 7 участков всего, записать и потом поанализировать. Я думаю, это очень быстро надоест =)
  • +1
    Прекрасно! Прекрасно! Только вчера запрос делал на тему тестовых камер, чтобы скрипт написать. Спасибо, задействую все каналы.
    Нужно ещё сервис запустить, где отмечать какие камеры кто пишет, чтобы покрыть наибольшее количество.

    Хоть хабр и вне политики — добавьте голосовалку:
    «За кого голосуете»
    — {список фамилий}
    — не пойду на выборы
    — испорчу бюллетень
    — я не из москвы

    В данном случае простительно, да и жутко любопытно

    • 0
      Мы нашли несколько серверов для записи и еще скоро будет программа для волонтеров (уже 150 человек). Программа будет раздавать УИКи для записи централизованно. Подробности будут тут: https://vk.com/prostovybory
      • 0
        Опп-па, я как раз над этим начал работать, давайте кооперироваться. Серверные мощности считайте безлимитны. Есть VPS хостинги, которые дают несколько дней демо режима, можно воспользоваться, если совсем плохо с ден. ресурсами.
  • 0
    Как оказалось, вроде как записи с камер можно официально из Ростелекома получить.
    • +2
      В Ростелекоме нужные записи могут и «потеряться».
    • +1
      Вроде только через некую процедуру. Просто так не дадут. Лучше писать всё сразу, хотя я не думаю что на участках будут фальсификации. Тут главное протоколы фотографировать, чтобы потом в избиркоме цифры не дописали.
      • 0
        По записи можно и количество проголосовавших посчитать, а потом сравнить с протоколоми «удивиться» например двукратному приросту в протоколе.
        • 0
          Протоколы тоже будем писать, в данный момент занят этим
    • 0
      Выдают по заявлению по полчаса видео за раз. Лучше самим записать.
  • +1
    Интересно, будет ли на этот раз записано что-то вроде вот этого и прочих танцев?
  • 0
    Запишем! Хостеры всех стран, присоединяйтесь! Очень нужны ресурсы, на счету каждый терабайт.
  • 0
    Как все сложно. Я к сожалению не оставил ссылок на хабр с прошлых выборов, но был мануал (обычный батник) по которому я без труда записал видео с двух камер своего участка. Все это в архиве занимает 3Гб (запись велась с утра до закрытия)Запись делалась почасовая, т.е. через 60 минут автоматом создавался новый файл формата vybory2012_uik81816cam2_04_194541.ts.
    Хорошо было бы что-нибудь подобное в этот раз сделать!
    • 0
      На выборах 04.03.2012 была очень удобная кроссплатформенная JAVA-программка, и очень подробный мануал к ней. Весь беспредел был удачно записан (правда толку...).
      Здесь же и правда слишком сложно для массовой записи.
      • 0
        Вы прикалываетесь чтоль? =) В скрипте нужно поправить 1 (одну) переменную. Куда проще-то?
        • 0
          Python, mongo, aria2c, tsmuxer, windows… Если каждый день работаешь с этими инструментами, то да — всё просто. Но под такой шаблон массовый пользователь явно не подпадает. Так что нет, не прикалываюсь.
          Даже мне понадобится время чтобы разобраться, как это всё запустить на Mac OS, но так сложилось, что времени как раз сейчас и совсем нет. Что уж говорить о среднестатистическом избирателе.
  • 0
    Есть скромненький домашний сервачок Ubuntu headless, C2D, 6Gb RAM, 1Tb винт и канал в 80Мбит.
    Еще есть желание пописать видео.
    Куда присоединяться?
    • 0
      Вот с этими ребятами попробуйте связаться: vk.com/prostovybory
  • 0
    А есть уже работающие камеры, на которых потестить можно?
    Пробовал wget скачивать, но ошибку 400 выдает.
    $ wget http://188.254.112.34/segment.ts?cid=a329391e-0678-11e3-bd4b-00163ebf8df9&var=orig&ts=1378448938-1378449589 [1] 30959 [2] 30960 sky@sky:~/test/NAVALNY$ --2013-09-06 15:27:36-- http://188.254.112.34/segment.ts?cid=a329391e-0678-11e3-bd4b-00163ebf8df9 Подключение к 188.254.112.34:80... соединение установлено. HTTP-запрос отправлен. Ожидание ответа... 400 Bad Request 2013-09-06 15:27:36 ОШИБКА 400: Bad Request.

    Или я что-то не так делаю?
    • 0
      Вы невнимательны в деталях. =) Таймстемпы с дробной частью должны быть. ___http://188.254.112.34/segment.ts?cid=a329391e-0678-11e3-bd4b-00163ebf8df9&var=orig&ts=1378448938.00-1378449589.00
      • 0
        Так я тоже пробовал — все равно Bad request.
        • +1
          Я бы сказал что в заголовках дело, ибо в браузере открывается.
          • 0
            Да, точно. Ну завтра покопаюсь с заголовками.
            Спасибо.
          • 0
            Они на referer смотрят.
            Вот так получилось:
            wget --header="Referer: http://vybory.mos.ru/" http://188.254.112.34/segment.ts?cid=a329391e-0678-11e3-bd4b-00163ebf8df9&var=orig&ts=1378449589.00-1378449600.00
  • 0
    Ребята с vk.com/prostovybory сделали программу для записи. Можно указать конкретные УИКи, а можно число рандомных, которые будут выделяться автоматом среди тех, которые еще никто не пишет.
    Скачать можно здесь: bitbucket.org/fak3/govstream-cli
    Если нужно под Windows, то вот ссылка: bitbucket.org/fak3/govstream-cli/downloads/govstream_0.3_win.rar
    • 0
      Прямые ссылки на версию могут протухнуть, поэтому ссылка на пост habrahabr.ru/post/192878/
  • 0
    На эти выборы собираетесь писать?
    Хорошо бы сделать полный видео-архив.
    • 0
      Нет, не собираюсь — в этом нет никакой практической цели. Записывать тысячи часов видео тоже никакого смысла нет.
      • 0
        Например, прогнать с помощью CV для обнаружения одинаковых лиц, затем через нейросеть для автоматического выделения ситуаций похожих на вброс.
        • 0
          Там качество камер слишком низкое, ну и камеры в основном висят под потолком, там лиц-то не видно. На прошлых выборах какие-то ребята писали анализировалку видео, которая позволяла посчитать, сколько бюллетеней бросили на участке и сравнивала эту цифру с итоговыми протоколами, было забавно.

          Но дело не в этом, дело в том, что я лично не понимаю, зачем это нужно. После каждых очередных выборов ютьюб полон видео с откровенными вбросами, каруселями, подделками протоколов и так далее, и чего? От этого что-то изменилось? В общем-то, мне своего времени на это жалко, если Вам, или кому-то еще эта задача кажется интересной — код же весь опубликован, дерзайте =)

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.