Захват видео с сетевых камер, часть 1

  • Tutorial
Сетевые видеокамеры постепенно вытесняют аналоговые, хоть и стоят они сейчас гораздо дороже. Сетевые обладают рядом очевидных приемуществ:
  • нет необходимости в отдельном регистраторе или плате захвата;
  • помехоустойчивость;
  • простая интеграция в существующую сеть;
  • нет ограничения по расстоянию;
  • наличие камер высокого разрешения;
  • просмотр камеры прямо с самой камеры по http;
  • наличие всевозможных настроек;
  • и др.

Нас интересует способ получения изображений с таких камер, для этого надо знать а как вообще они их передают? На наше счастье камеры используют существующие стандарты, а не то, что взбредёт в голову китайскому разработчику. Подавляющее большинство камер используют один или несколько способов передачи видео, это в основном Motion JPEG по HTTP, Motion JPEG по RTSP или H264 по RTSP. Также многие камеры могут передавать звук, но он нас не интересует сейчас.

В этой статье я рассмотрю эти способы передачи изображений с сетевых камер, а также приведу пример захвата таких изображений всё на том же Python'е.

MJPEG over HTTP


Самый простой способ передачи картинки — это MJPEG по HTTP. В этом случае кадры отдаются готовыми JPEG файлами через специальные разделители. Для таких случаев был разработан специальный MIME тип multipart. У него есть несколько подтипов, нас интересуют mixed и x-mixed-replace. Отличий между ними практически нет, мы их будем обрабатывать абсолютно одинаково. Отличие у них смысловое: mixed указывает просто на документ, состоящий из нескольких частей, эти части могут быть независимыми или могут объединяться; а тип x-mixed-replace прямо указывает, что каждая следующая часть должна заменять собой предыдущую и обрабатываться должны как обновление какого-то представления. Буква «x» вначале названия указывает на то, что этот тип является экспериментальным, но тем не менее он во всю используется.

В HTTP заголовке тип MIME указывается в Content-Type параметре:

Content-Type: multipart/mixed; boundary="some_boundary"

или

Content-Type: multipart/x-mixed-replace; boundary=other_boundary

У этих типов есть обязательный параметр boundary, который указывает каким текстом разделяются части документа. К разделителю также добавляются два дефиса в его начало. Важно чтобы этот разделитель не встречался в самом сообщении, если не указывается его размер в Content-Size.

Чтобы понять структуру передачи MJPEG по HTTP достаточно взглянуть на следующий пример:

HTTP/1.0 200 OK
Connection: close
Server: MJPG-Streamer/0.2
Cache-Control: no-store, no-cache, must-revalidate, pre-check=0, post-check=0, max-age=0
Pragma: no-cache
Expires: Mon, 3 Jan 2000 12:34:56 GMT
Content-Type: multipart/x-mixed-replace;boundary=boundarydonotcross

--boundarydonotcross
Content-Type: image/jpeg
Content-Length: 23950
X-Timestamp: 0.000000

%Binary JPEG%
--boundarydonotcross
Content-Type: image/jpeg
Content-Length: 24756
X-Timestamp: 0.000000

%Binary JPEG%
--boundarydonotcross
Content-Type: image/jpeg
Content-Length: 23950
X-Timestamp: 0.000000

%Binary JPEG%

Для анализа того, как ваша сетевая камера передаёт изображения, можно воспользоваться либо снифером (я пользуюсь WireShark), либо подключиться к камере через telnet, например так:

$ telnet 192.168.0.50 80
Trying 192.168.0.50..
Connected to 192.168.0.50.
Escape character is '^]'.
GET /jpeg HTTP/1.1


После строки GET нужно ещё отправить одну пустую строку для индикации того, что ваш заголовок закончился. А вместо "/jpeg" нужно написать тот запрос, по которому ваша камера отдаёт MJPEG.

В приведённом выше примере %Binary JPEG% соответствует интересующей нас информации — JPEG изображению. Его-то нам и нужно выделить из потока.

Как видно вначале идёт стандартный HTTP заголовок с описанием документа. Connection может быть как close, так и keep-alive, в нашем случае это не играет роли. Из заголовка нам всего навсего нужно две строчки: первая со статусом 200 OK, которая говорит нам, что всё хорошо, сейчас вылетит птичка; и Content-Type для определения параметра boundary.

После HTTP заголовка (после пустой строки) идёт тело передаваемого документа, состоящего из множества частей. Каждая часть начинается с разделителя, имеет свой собственный заголовок и своё тело документа после пустой строки. Content-Type: image/jpeg указывает нам, что мы действительно получаем JPEG изображения, Content-Length на размер текущего кадра в байтах (в первой части это 23950 байт), а в X-Timestamp может передаваться временная отметка текущего кадра, можно использовать для этих целей текущее время компьютера в момент приёма кадра, но X-Timestamp будет точнее, так как сеть может оказывать разное влияние на скорость передачи кадров.

Python MJPEG over HTTP Client


Несмотря на простой формат передачи изображений, их приём можно реализовать разными методами. Также играет роль TCP сегментация, а точнее подход к её обработке. Дело в том, что максимальный размер передаваемого сообщения (MTU) по Ethernet не может превышать 1500 байт и данные мы обрабатываем каждый раз когда такой пакет к нам приходит. Если анализировать информацию сразу по её приходу, то может случится так, что данные будут не полными и парсер не сможет справится со своей задачей. А если буферизировать поток входящих данных, то это не лучшим образом сказывается на производительности и ресурсоёмкости. Надежнее было бы воспользоваться буферизацией и приступать к анализу только когда уже накоплено достаточно информации (сначала нужно прочитать до '\r\n\r\n', чтобы выделить заголовок, а потом либо до тех пор пока не встретятся два раза в потоке разделители, либо также читать до пустой строки, определять размер изображения и отсчитывать количество байт). Но я воспользовался методом обработки информации сразу по её приходу.

Код клиента состоит из двух файлов: main.py и http_mjpeg_client.py. В первом производится запуск приложения, а во втором реализована работа с камерой. Сразу их и приведу здесь.
main.py
from twisted.internet import reactor
from http_mjpeg_client import MJPEGFactory

def processImage(img):
    'This function is invoked by the MJPEG Client protocol'
    # Process image
    # Just save it as a file in this example
    f = open('frame.jpg', 'wb')
    f.write(img)
    f.close()
    
def main():
    print 'Python M-JPEG Over HTTP Client 0.1'
    # Define connection parameters, login and password are optional.
    config = {'request': '/mjpeg',
              'login': 'admin',
              'password': 'admin',
              'ip': '127.0.0.1',
              'port': 8080,
              'callback': processImage}
    # Make a connection
    reactor.connectTCP(config['ip'], config['port'], MJPEGFactory(config))
    reactor.run()    
    print 'Python M-JPEG Client stopped.'

# this only runs if the module was *not* imported
if __name__ == '__main__':
    main()

http_mjpeg_client.py
from twisted.internet.protocol import Protocol, ClientFactory
from base64 import b64encode
import re

debug = 1

class MJPEGClient(Protocol):
    def __init__(self):
        # A place for configuration parameters
        self.config = {}
        # I we are connected to a web server
        self.isConnected = False
        # The boundary in multipart stream
        self.boundary = ''
        # Actual image data goes here
        self.img = ''
        # Size of the image frame being downloaded
        self.next_img_size = 0
        # Indicates that currently parsing a header
        self.isHeader = False

    def connectionMade(self):
        # Implement basic authorization
        if self.config['login']:
            authstring = 'Authorization: Basic ' + b64encode(self.config['login']+':'+self.config['password']) + '\r\n'
        else:
            authstring = ''
        # Form proper HTTP request with header
        to_send = 'GET ' + self.config['request'] + ' HTTP/1.1\r\n' + \
            authstring + \
            'User-Agent: Python M-JPEG Client\r\n' + \
            'Keep-Alive: 300\r\n' + \
            'Connection: keep-alive\r\n\r\n'
        # Send it
        self.transport.write(to_send)
        if debug:
            print 'We say:\n', to_send
    
    def dataReceived(self, data):
        if debug:
            print 'Server said:\n', len(data), 'bytes of data.'
        if not self.isConnected:
            # Response header goes before empty line
            data_sp = data.strip().split('\r\n\r\n', 1)
            header = data_sp[0].splitlines()
            # Parse header
            for line in header:
                if line.endswith('200 OK'): # Connection went fine
                    self.isConnected = True
                    if debug: print 'Connected'
                if line.startswith('Content-Type: multipart'): # Got multipart
                    r = re.search(r'boundary="?(.*)"?', line)
                    self.boundary = r.group(1) # Extract boundary
                    if debug: print 'Got boundary:', self.boundary
            # If we got more data, find a JPEG there
            if len(data_sp) == 2:
                self.findJPEG(data_sp[1])
        else:
            # If connection is alredy made find a JPEG right away
            self.findJPEG(data)
    
    def findJPEG(self, data):
        hasMoreThanHeader = False
        # If we know next image size, than image header is already parsed
        if not self.next_img_size:
            # Otherwise it should be a header first
            for line in data.splitlines():
                if line == '--'+self.boundary:
                    self.isHeader = True
                    if debug: print 'Got frame header'
                elif line == '':
                    if self.isHeader:
                        # If we might have more data after a header in a buffer
                        hasMoreThanHeader = True
                    self.isHeader = False
                elif self.isHeader:
                    # Here we can parse all the header information
                    # But we are really interested only in one
                    if line.startswith('Content-Length:'):
                        self.next_img_size = int(line.split(' ')[1])
                        if debug: print 'Next frame size:', self.next_img_size
                        
        else:
            # How many bytes left to read
            remains = self.next_img_size - len(self.img)
            self.img += data[:remains]
            # We got the whole image
            if len(self.img) == self.next_img_size:
                if debug: print 'Got a frame!'
                # Run a callback function
                self.config['callback'](self.img)
                # Reset variables
                self.img = ''
                self.next_img_size = 0
            # If something left in a buffer
            if data[remains:]:
                self.findJPEG(data[remains:])
        if hasMoreThanHeader:
            data_sp = data.split('\r\n\r\n', 1)
            # If there is something after a header in a buffer
            if len(data_sp) == 2:
                self.findJPEG(data_sp[1])

    def connectionLost(self, reason):
        print 'Connection lost, reconnecting'
        self.isConnected = False
        self.img = ''
        self.next_img_size = 0
        self.isHeader = 0
        self.boundary = ''

class MJPEGFactory(ClientFactory):
    def __init__(self, config):
        self.protocol = MJPEGClient
        self.config = config

    def buildProtocol(self, addr):
        prot = ClientFactory.buildProtocol(self, addr)
        # Weird way to pass the config parameters to the protocol
        prot.config = self.config
        return prot

    def clientConnectionLost(self, connector, reason):
        # Automatic reconnection
        connector.connect()

В главном файле мы определяем параметры подключения к камере в словаре config, запускаем реактор сетевого фрэймворка Twisted и обрабатываем получаемые изображения в функции processImage(). В этом примере каждый получаемый кадр просто записывается в текущую директорию с именем frame.jpg.

Работоспособность я проверял с помощью MJPEG стримера, запускал я его вот так:
./mjpg_streamer -i "./input_testpicture.so" -o "./output_http.so -w ./www"
При этом request в конфигурации клиента надо указать равным /?action=stream.
Передавать изображения с вебкамеры он у меня отказался.

Я старался хорошо документировать второй файл, чтобы читателю было легче понять как происходит процесс изъятия изображений из потока. На словах алгоритм можно описать следующим образом: при подсоединении к камере первым делом формируем HTTP заголовок с обращением к ней и отсылаем его, это функция connectionMade(). Функция dataReceived() вызывается всякий раз, когда к нам приходят новые данные. В ней мы проверяем установлена ли уже передача JPEG данных или нет. Если ещё нёт, то значит к нам должен прийти HTTP заголовок ответа камеры, мы его выделяем с помощью функции split('\r\n\r\n', 1), затем разбираем его по полочкам, выделяя нужные параметры (статус и boundary). В остальных случаях мы полученные данные сразу передаём функции findJPEG().

В этой функции так же происходит ветвление в зависимости от того получили ли мы внутренний заголовок JPEG документа или ещё нет. Если не получили — ожидаем его и разбираем, если получили — значит данные это непосредственно JPEG изображение и складываем их в переменную self.img до тех пор пока не получим все self.next_img_size байт изображения, а когда получим — вызываем функцию, переданную нам через параметр конфигурации callback, и отдаём ей только что полученное изображение.

Параметр debug можно выставить в ноль для отключения отображения выводимой информации.

Загрузить исходный код можно по этой ссылке: Python MJPEG over HTTP Client.

Продолжение следует…

Полезно почитать:


MIME
Motion JPEG
Список заголовков HTTP

P.S.: Я решил разбить статью на две части, так как единым целом она получается довольно объёмной, а я не хочу её перегружать для лучшего понимания и большего удобства.
Share post
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 81

  • UFO just landed and posted this here
      +1
      Пожелание принято, переведу завтра.
        +6
        Ни в коем случае не переводите.
          0
          интересная реакция.
          а как это аргументируется?
          0
          Не стоит.
            0
            > Пожелание принято, переведу завтра.

            Ну тогда и код на Brainfuck переведите…
            +7
            Переходите на 1С
            • UFO just landed and posted this here
                0
                Я наивно полагаю, что разработчику на чем угодно кроме 1С стоило бы знать английский. Арабские комментарии я бы тоже попросил перевести.
                • UFO just landed and posted this here
                    0
                    Ничего не понял. Статья на русском языке, весь текст на русском. Код и комментарии на английском, по вашему надо и код на русский переписать. :)
                    Я спокойно читаю статьи на английском, не вижу в этом проблемы, но русские комментарии… Представьте, что вам попал код с китайскими комментариями. Первая реакция?
                    • UFO just landed and posted this here
            • UFO just landed and posted this here
              +1
              Случайно не подскажете, есть ли дешевые варианты получения стереопотока с двух камер (минимум 15-20 fps, хотя бы 640*480). Firewire дорогие, usb2 — не хватает скорости шины.
                0
                usb2 тащит 20 мб в секунду спокойно, жатый в мпег поток туда влезет в лет.
                  0
                  Поддерживаю. Другая проблема в том, что простые камеры обычно сжимают минимально, поток получается действительно высокий.
                    +1
                    Камера из обзора DCS-2102 поддерживает сжатие MPEG-4, так что поток получается вполне вменяемый + умеет писать по датчику движения, что тоже экономит место.
                  0
                  Обычные usb вебкамеры могут справится с этой задачей только при хорошем освещении.
                  В вашем случае дешевле будет использоваться аналоговые камеры и плату захвата (с полной скоростью захвата на каждый канал). Я использовал платы ORIENT на чипе от philips.
                  0
                  Поставил когда то себе в офисе камеры на MJPEG (Dlink DCS-910 и 920), когда появилась надобность записывать видео, то столкнулся с тем что дохрена места кушает 6 камер в сутки. Большой недостаток MJPEG камер.
                    0
                    В mpeg4 в более дорогих длинках очень качество падает.
                      +5
                      Была схожая проблема. Решил следующим образом:
                      Сервер сильно нагруженный днем, ночью простаивал. После конца рабочего дня сервер брал видео с схд и конвертировал в более приемлемый формат.
                      Через некоторое время перешел к варианту с двойной переконвертацией видео: если с момента съемки прошла неделя, то видео пережималось с мешьним качеством и fps. Это позволяло иметь детальную съемку последней недели и большой период архива
                      0
                      Было бы круто, если бы был пример вставки своего видео в онлайн-трансляцию, то есть, чтобы вебка «показывала» не реалтайм, а видео, которое лежит на винте.
                        0
                        Было бы круто, но причем тут тема топика?
                        Хотите стримить видео — берите VLC (например) и вперед.
                          0
                          Ещё вещать видео в сеть может ffserver.
                          0
                          А мы вот поменяли барахло от D-link на аналоговые с матрицами SONY. Картинка D1/25FPS. Разрешение хоть и меньше, но в целом система куда стабильнее и производительнее.

                          За те деньги, что будет стоять качественная LAN-камера, можно купить аналоговую 520 twl, infra-red, варифокал + защитный кожух.
                            +1
                            Тогда еще комп нужен и железяки для захвата видео. А прелесть серевых камер именно в том, что ничего кроме них самих не надо.
                              0
                              Комп не нужен, отдельный видеорегистратор гораздо удобнее, к тому же многие модели имеют сеть и веб-интерфейс. Кривизна веб-интерфейса варьируется в зависимости от производителя и модели.
                                0
                                Плата видеозахвата + комп + ПО обойдутся дороже, нежели регистратор. К тому же видеорегистратор более производительней и имеет сетевой интерфейс и просто таки гору примочек.
                                0
                                Да, действительно, качественные сетевые камеры стоят намного дороже качественных аналоговых. В этом их минус.
                                +2
                                Камеры длинка умеют сами писать на шару виндовую, не проще ли это? И у них есть датчик движения что позволит дополнительно экономить место.
                                  0
                                  какие модели?
                                    0
                                    Модель из обзора DCS-2102
                                      0
                                      У DCS-2102 белые ножки, а эта модель что на фотке — DCS-910 )
                                        0
                                        У меня была только старая модель 910 серии она была в виде цилиндра, достал из шкафа 2102 и в правду ножка белая, другой формы и металлическая.
                                          0
                                          на работе валяются цилиндрические — DCS-900
                                      0
                                      Да почти во всех д-линках эта опция была…
                                      0
                                      В статье рассматривается программный способ захвата для того, чтобы можно было написать свою программу, которая бы обрабатывала видео.
                                        0
                                        А теперь ситуация, 8 камер начнут писать видео 25 fps. Нужен выделенный комп с хорошей дисковой подсистемой.
                                          0
                                          Не понял, к чему это?
                                            0
                                            >> Камеры длинка умеют сами писать на шару виндовую, не проще ли это? И у них есть датчик движения что позволит дополнительно экономить место.

                                            Попробуй включить 8 ip-камер и пускай пишут на винт ) посмотри загрузку ) у аналоговых на регистраторе тоже запись по движению как правило, либо в случае сработки сигнализации она может постоянно писать.
                                              0
                                              Два мегабита на поток, 8 потоков будет 16 мегабит, думаю будет не сильно сложно.
                                                0
                                                Думаю/полагаю — это теория, на практике попробуй, и всё поймёшь.
                                                  0
                                                  На практике в линуксе с этим проблем нет.
                                                  Вы поток в мпег4 жмете?
                                                  Дайте перфмон с винды где вы так пишите и у вас что то загибается.
                                                    0
                                                    С linux я не спорю. Я о окнах говорю. Сейчас всё на DVR поставлено, там linux, проблем нет.
                                                      0
                                                      Надо 2008 винду попробовать, там с файловым вводом выводом вроде получше ситуация.
                                                        0
                                                        linux бесплатен, а за окна дополнительно платить нужно :)
                                        +1
                                        Сетевые видеокамеры постепенно вытесняют аналоговые


                                        Может все ж таки цифровые? =)
                                          +1
                                          Нецифровых сетевых камер я ещё не видел. Здесь упор именно на слово сетевое. Потому что цифровые камеры могут подключаться к компьютеру через USB или FireWire.
                                            +1
                                            Один фиг: сетевое устройство — не антоним аналогового.
                                          0
                                          Ни кто китайские ip-камеры не заказывал? Хочу для теста взять, уж очень вкусная цена, стоит 50$.
                                            0
                                            Честно — не видел китайских ип-камер… Подозревал, но… Ссылкой не поделишься?
                                              0
                                              Нашел здесь Доставка, вроде, бесплатная. В описании указанно что используется M-JPEG.
                                                0
                                                Сенкс — мб пригодится ещё…
                                              0
                                              Брали, только такие в принципе не плохо, все что надо есть, даже местами лучше всяких AXIS и D-Link. Минус с софтом идущим в комплекте, в режиме записи по детектору движения пишет через раз, а то и вообще не пишет. Пробовал с Zoneminder все отлично, но из-за одной камеры не будешь сервер линуксовый держать.
                                                0
                                                Как по срокам с доставкой?
                                                  0
                                                  первые брали прямо в Китае отличаются только коробкой. С dealextreme ждем 3 неделю.
                                              0
                                              Вот на счет пары пунктов о преимуществах хочу поспорить. Работаю в фирме, что занимается системами безопасности, в том числе охранным видеонаблюдением, так вот, расстояние ограничено как у ip камер, так и у аналоговых. У ip камер расстояние диктуется 100 метрами (+-) между активными сетевыми устройствами, то есть без применения дополнительных свитчей камеру далеко не установишь. Эта проблема пропадает когда сеть построена уже до того, как вы захотели поставить видеонаблюдение и заказчик позволил сделать вам это. У аналоговых камер, на добротном кабеле можно отнести камеру и на 400 метров, если нужно больше, то так же можно использовать усилители, или сегментировать систему на зоны во главе каждой ставить свой регистратор и соединять их между собой по ethernet'у. Следующий пункт про HD видео, разумеется оно существует и в аналоговом мире (вот например).

                                              В целом ни одна дешевая ip камера по качеству картинки и удобству работы не сравниться с самыми дешевыми камерами и регистраторами из аналогового сегмента.
                                                0
                                                С другой стороны — если на углу здания две камеры, то надо тянуть два кабеля для аналоговой, а с цифрой — поставить хаб…

                                                Кстати, а есть аналоговые свичи или квадраторы для аналоговых камер цветных этак на 11-12 каналов???
                                                  0
                                                  >Кстати, а есть аналоговые свичи или квадраторы для аналоговых камер цветных этак на 11-12 каналов???
                                                  Что такое «аналоговые свичи»?
                                                    0
                                                    Переключатели каналов для аналоговых камер: это когда у тебя камер с десяток, а вывести надо картинку с одной из, причём с конкретной.
                                                      0
                                                      Может и есть, только они мало кому нужны, т.к. современные видеорегистраторы могут выводить на монитор как несколько каналов сразу, так и любой по выбору.
                                                    –1
                                                    Хаб будет питаться воздухом? РРоЕ дорогие + на хаб нужно будет защитный кожух.
                                                    +1
                                                    >Следующий пункт про HD видео, разумеется оно существует и в аналоговом мире (вот например).
                                                    Не существует. По Вашей ссылке — SDI-камеры, а это цифровой стандарт.
                                                      0
                                                      Имею ввиду, что передача изображения от камеры происходит по коксу через композит.
                                                        0
                                                        Так ведь и аналоговый видеосигнал успешно передается по витой паре, однако он не становится от этого цифровым.
                                                          0
                                                          По витой паре аналоговый сигнал передается через специализированные устройства, в простонародье — приемо-передатчики.

                                                          Что имеем (камера)->(Композитный разъем)--------(коаксиальный кабель)------>(Композитный разъем)->(Видеорегистратор).

                                                          Самые простые KPC камеры по идее классифицируются как цифровые устройства, но сигнал на выходе получает ярлык аналогового, видеорегистратор тоже не на кассеты пишет, но сигнал он принимает, все же, аналоговый.

                                                          Боюсь мы сейчас запутаемся в понятиях и терминах. Так что пусть все вышесказанное остается моим личным мнением.
                                                    0
                                                    А какой-нибудь неплохой, и желательно, не очень дорогой софт для видеозахвата + просмотра по архиву можете посоветовать?
                                                      0
                                                      zoneminder.
                                                      20 ip камер типа DCS-900(MJPEG) держит без проблем.
                                                        0
                                                        поддерживаю.
                                                        0
                                                        Пользовался Zoneminder и AVReg, последний больше понравился, но у него ограничение — бесплатная версия только на 4 канала.
                                                        0
                                                        Используем AXIS. Дорогие, но есть крутые возможности, например они реагируют на движение и отсылают снимки на FTP. Можно организовать двухстороннее общение, если приделать к камере аудиоколонку.

                                                        Есть даже развитое API, через которое можно управлять кучей параметров, например, поворачивать камеру.
                                                        Но это сыграло с нами злую шутку. Мы отсылали команду на поворот каждую минуту, а потом оказалось, что MBTF камеры на повороты в таком случае — всего 100 дней :) Пришлось от этого отказаться.
                                                          0
                                                          MBTF?
                                                            0
                                                            Mean time between failures — время наработки на отказ.
                                                            Сломали камеру, в общем :)
                                                            0
                                                            MTBF (MBTF — аж глаз режет)
                                                            0
                                                            В Москве, в Ростиксах такие камеры по штук 30 висят, может кто знает как у них это все утроено?
                                                              +1
                                                              муляжи?
                                                              0
                                                              home-supervision.com
                                                                +1
                                                                По случайному стечению обстоятельств статья пришлась очень кстати, так как понадобилось работать с камерой Linksys WVR200. Причем скрипт вроде как завелся с пол-оборота, подключение устанавливал, что-то принимал, но кадры не писал. Спустя пару часов обнаружил, что в стартовом пакете камера отдает «Content-type: multipart/x-mixed-replace;boundary=ThisRandomString», то есть type с маленькой буквы, а скрипт рассчитывает на заглавную.
                                                                Кстати была обещана вторая часть…
                                                                  0
                                                                  Рад, что статья помогла. Хм, теперь буду делать всё в lowercase, спасибо за feedback.
                                                                  Вторая часть выйдет в субботу 23 числа про захват с RSTP камер.
                                                                  0
                                                                  Кстати, не стоит писать про то, что «сетевые камеры вытесняют аналоговые».
                                                                  Так получилось, что денег в IP-камеры вложили порядочно, но для _охранных_ задач они лишь обуза.
                                                                    0
                                                                    Соглашусь, не везде им место.

                                                                  Only users with full accounts can post comments. Log in, please.