Sony Camera Remote API, медленная потоковая передача в режиме реального времени

Я пытаюсь транслировать видео в реальном времени с камеры SONY FDR-X1000V на рабочий стол. Я использую python для вызова API и загрузки пакета и использую opencv для декодирования jpeg. Когда я запускаю его, он с трудом ловит один кадр в секунду. Позже я узнал, что размер полезной нагрузки jpeg может составлять 8 МБ. Однако FDR-X1000V не поддерживает изменение размера изображения в реальном времени. Но когда я использую приложение на iPhone для просмотра в реальном времени, все идет гладко. Итак, вот мой вопрос: 1. Нормально ли, что полезная нагрузка jpeg может достигать 8 МБ? 2. Если да, то как я могу плавно транслировать видео в реальном времени?

Вот мой код:

try:
    result = api.do('startLiveview')
    url = result['result'][0]
except KeyError:
    print result
f = urllib2.urlopen(url)
buff = ''
chunk_size = 32768
for i in xrange(3000):
    if len(buff) < chunk_size:
        time_s = time.time()
        buff = buff + f.read(chunk_size)
        print "Download Speed %f KB/s"%(chunk_size/1000/(time.time() - time_s))
    time_s = time.time()
    start_code = ''.join(buff).find('$5hy')
    # print "LCS time cost", time.time() - time_s

    if start_code < 0:
        buff = buff[-12:]
        print "skip", len(buff)-12
    elif start_code < 8:
        buff = buff[8:]
    else:
        if start_code > len(buff) - 129:
            buff = buff + f.read(chunk_size)
        payload_type = ord(buff[start_code-7])
        payload_size, = struct.unpack('<I', buff[start_code+4:start_code+8].ljust(4,'\0'))
        padding_size = ord(buff[start_code+8])
        print "Type:%d\tPayload:%d\tPadding:%d\t"%(payload_type,payload_size,padding_size)

        buff = buff[start_code+128:]
        if payload_type == 1:
            if payload_size + padding_size > len(buff):
                time_s = time.time()
                download_size = payload_size+padding_size-len(buff)
                buff = buff + f.read(download_size)
                print "Download Speed %f KB/s"%(download_size/1000/(time.time() - time_s))
            img_data = buff[:payload_size]
            buff = buff[payload_size:]

            time_s = time.time()
            d = np.asarray(bytearray(img_data), dtype='uint8')
            img = cv2.imdecode(d,cv2.IMREAD_COLOR)
            cv2.imshow('postview',img)
            cv2.waitKey(30)
            # print "Decode time cost", time.time() - time_s

Некоторый вывод:

Type:1  Payload:8410624 Padding:0   
Download Speed 679.626326 KB/s

person Jason    schedule 07.04.2016    source источник


Ответы (3)


Немного бесстыдной рекламы - но это может вам помочь. Вы пробовали с кодом pysony?

Вам придется вручную установить QX_ADDR, но это должно дать вам «более быстрый» поток в реальном времени... https://github.com/Bloodevil/sony_camera_api/issues/22

В примере pygameLiveView есть необходимые строительные блоки, вам просто нужно настроить загрузку jpeg для загрузки в openCV вместо pygame.

person Simon Wood    schedule 07.04.2016
comment
Я пробовал это pysony, однако они не реализуют ресинхронизацию для 4-байтового стартового кода. Это может привести к ошибкам на практике. - person Jason; 08.04.2016

Я полагаю, что проблема может заключаться в том, что вы анализируете «Размер данных полезной нагрузки без размера заполнения» в «Заголовке полезной нагрузки LiveView» как 4 байта данных вместо 3 байтов данных. Таким образом, в этом случае размер полезной нагрузки выглядит больше, чем ожидалось. Это объясняется в документации в разделе «Формат контейнера JPEG для данных просмотра в реальном времени». Попробуйте изменить это и дайте мне знать, если это все еще не работает для вас.

person Robert    schedule 08.04.2016
comment
Да, вы правы, но самое главное, что не упоминается в документе, это про прямой и обратный порядок байтов. Здесь, в моем коде, я предположил, что это прямой порядок байтов, из-за которого большинство вещей не работает. Интересным фактом является то, что, хотя я не вычисляю размер полезной нагрузки (который всегда больше реального размера), OpenCV всегда может декодировать изображение, что заставляет меня поверить, что размер полезной нагрузки правильный. - person Jason; 09.04.2016

Ответ от @Simon Wood и @Robert - Sony очень полезна! Я сделал две ошибки в своем коде.

Во-первых, размер полезной нагрузки составляет 3 байта вместо 4 байтов.

Во-вторых, я предположил, что порядок байтов — прямой, а не прямой.

Проект, упомянутый @Simon Wood, — хороший. Хотя он не реализует ресинхронизацию 4-байтового стартового кода.

Чтобы сделать это правильно, я пишу свои собственные коды двумя способами для проверки и повторной синхронизации:

try:
    result = api.do('startLiveview')
    url = result['result'][0]
except KeyError:
    print result
f = urllib2.urlopen(url)

#method 1
buff = ''
chunk_size = 8192
for i in xrange(300):
    if len(buff) < chunk_size:
        time_s = time.time()
        buff = buff + f.read(chunk_size)
        # print "Download Speed %f KB/s"%(chunk_size/1000/(time.time() - time_s))
    time_s = time.time()
    start_code = ''.join(buff).find('$5hy')
    # print "LCS time cost", time.time() - time_s

    if start_code < 0:
        buff = buff[-12:]
        print "skip", len(buff)-12
    elif start_code < 8:
        buff = buff[8:]
        print "skip a header"
    else:
        if start_code > len(buff) - 129:
            buff = buff + f.read(chunk_size)
        start_byte = ord(buff[start_code - 8])
        payload_type = ord(buff[start_code - 7])
        sequence_num, = struct.unpack('>I', buff[start_code - 6:start_code - 4].rjust(4,'\0'))
        time_stamp, = struct.unpack('>I', buff[start_code - 4:start_code].rjust(4,'\0'))
        payload_size, = struct.unpack('>I', buff[start_code+4:start_code+7].rjust(4,'\0'))
        padding_size = ord(buff[start_code+8])
        print "StartByte:%d\t sequenceNum:%d\t timeStamp:%d\t Type:%d\t Payload:%d\t Padding:%d\t"%(
            start_byte,sequence_num,time_stamp,payload_type,payload_size,padding_size)

        buff = buff[start_code+128:]
        if payload_type == 1:
            if payload_size + padding_size > len(buff):
                time_s = time.time()
                download_size = payload_size+padding_size-len(buff)
                buff = buff + f.read(download_size)
                # print "Download Speed %f KB/s"%(download_size/1000/(time.time() - time_s))
            img_data = buff[:payload_size]
            buff = buff[payload_size:]

            time_s = time.time()
            d = np.asarray(bytearray(img_data), dtype='uint8')
            img = cv2.imdecode(d,cv2.IMREAD_COLOR)
            cv2.imshow('postview',img)
            cv2.waitKey(10)
            # print "Decode time cost", time.time() - time_s

#method 2
def checkbyte(f):
    if f.read(4) == '$5hy':
        return
    state = 0
    i = 1
    while 1:
        i+=1
        if state == 0 :
            if f.read(1) == '$':
                state = 1
            else:
                state = 0
        if state == 1 :
            if f.read(1) == '5':
                state = 2
            else:
                state = 0
        if state == 2 :
            if f.read(1) == 'h':
                state = 3
            else:
                state = 0
        if state == 3 :
            if f.read(1) == 'y':
                state = 4
            else:
                state = 0
        if state == 4 :
            print 'skip', i
            return
for i in xrange(300):
    buff = f.read(8)
    start_byte ord(buff[0])
    payload_type, = struct.unpack('>I',buff[1].rjust(4,'\0'))
    sequence_num, = struct.unpack('>I',buff[2:4].rjust(4,'\0'))
    time_stamp, = struct.unpack('>I',buff[4:8])

    #payload header
    checkbyte(f)
    buff = f.read(124)
    payload_size, = struct.unpack('>I',buff[0:3].rjust(4,'\0'))
    padding_size= ord(buff[3])

    print "StartByte:%d\t sequenceNum:%d\t timeStamp:%d\t Type:%d\t Payload:%d\t Padding:%d\t"%(
            start_byte,sequence_num,time_stamp,payload_type,payload_size,padding_size)
    d = f.read(payload_size)
    if padding_size > 0:
        f.read(padding_size)

    if payload_type == 1:
        # Type = 0x01
        d = np.asarray(bytearray(d), dtype='uint8')
        img = cv2.imdecode(d,cv2.IMREAD_COLOR)
        cv2.imshow('postview',img)
        cv2.waitKey(1)

print api.do('stopLiveview')
person Jason    schedule 08.04.2016