Печать кадра MJPEG

Я пытаюсь сделать стример mjpeg. Первые 5 байтов, предшествующих кадру, определяют его размер. Затем я могу извлечь кадр. Мне нужно проверить, получил ли я правильный кадр. Я пытаюсь записать кадр в файл .jpeg; Это не работает. Я делаю это правильно?

import os
from array import array

class VideoStream:
    def __init__(self,filename):
        self.fis = open(filename,'r')
        self.frame_nb = 0

    def getnextframe(self):
        length = 0
        frame_length = bytearray(5)

        frame_length = self.fis.read(5)
        fm = array('B',frame_length[:5])

        length = fm[4]+((fm[3]<<8)&0xFF)+((fm[2]<<16)&0xFF)+((fm[1]<<24)&0xFF)+((fm[0]<<32)&0xFF)

        frame = self.fis.read(length)
        print 'len=',length

        test = open("test.jpeg",'w')
        test.write(frame)
        test.close()
        print 'frame=',frame



if __name__=='__main__':
    vs = VideoStream("Movie.mjpeg")
    vs.getnextframe()

person sudeepdino008    schedule 09.04.2015    source источник


Ответы (1)


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

Можно реализовать простой дополнительный тест, если данные кадра начинаются с маркера начала изображения JPEG (FF D8) и заканчиваются маркером конца изображения (DD D9).

Следующая функция должна выполнять итерацию по изображениям JPEG, разделенным длиной кадра из пяти символов ascii, например VideoStream.java, который вы портировали на Python:

def iter_frames(filename):
    with open(filename, 'rb') as mjpeg_file:
        while True:
            frame_length_bytes = mjpeg_file.read(5)
            if len(frame_length_bytes) != 5:
                if frame_length_bytes:
                    raise ValueError('incomplete length')
                else:
                    break
            frame_length = int(frame_length_bytes)
            frame = mjpeg_file.read(frame_length)
            if len(frame) != frame_length:
                raise ValueError('incomplete frame data')
            if not (
                frame.startswith(b'\xff\xd8') and frame.endswith(b'\xff\xd9')
            ):
                raise ValueError('invalid jpeg')

            yield frame


def main():
    frames = iter_frames('Movie.mjpeg')
    frame = next(frames)
    with open('test.jpg', 'wb') as jpeg_file:
        jpeg_file.write(frame)



if __name__ == '__main__':
    main()

Он проверяет, являются ли как значение счетчика байтов, так и данные JPEG полными, и присутствуют ли начальные и конечные маркеры JPEG.

Гораздо проще, чем вы думали, я думаю. Но есть одна загвоздка: этот формат, скорее всего, придумал автор этого Java-класса.

MJPEG — это просто видеокодек, который представляет собой просто объединенные изображения JPEG. Но он очень редко приходит в этом «сыром» формате, а встроен в формат контейнера с метаинформацией, такой как данные MJPEG, частота кадров, возможно, звук и так далее.

Одним из таких форматов является AVI, как в файле MJPEG avi, который вы сослался в комментарии.

Извлечение кадров из такого файла в отдельные изображения JPEG — это немного больше работы, чем чтение изображений JPEG с префиксом простой информации о длине, а затем объединенных в один файл. Нужно реализовать программу чтения AVI, которая достаточно понимает формат AVI, чтобы получить данные кадра. Затем программа для чтения JPEG, которая достаточно понимает формат JPEG, чтобы читать полный кадр, поскольку они сохраняются подряд без какой-либо информации о длине.

Следующая проблема заключается в том, что не все файлы MJPEG содержат кадры, которые можно использовать как отдельные изображения JPEG. В некоторых отсутствует таблица данных (таблица Хаффмана), необходимая для распаковки данных изображения. В спецификации AVI для кодека MJPEG есть фиксированная таблица. Эта таблица используется программным обеспечением для декодирования и должна быть введена в кадр при сохранении в виде файла JPEG.

И последнее «вещь»: есть видео с чересстрочной разверткой, которые не содержат полных изображений, но два последовательных изображения должны быть объединены в одно. Каждое изображение содержит каждую вторую строку. Ваш предоставленный пример MJPEG avi это такое видео. При извлечении кадров без декодирования, деинтерлейсинга и перекодирования каждое изображение будет вдвое меньше высоты видео.

Чтобы получить лучшее представление о том, как выглядят отдельные изображения, эта командная строка ffmpeg извлекает данные кадра и вставляет отсутствующую таблицу данных, чтобы получить отдельные изображения JPEG:

ffmpeg -i bowlerhatdancer.sleepytom.SGP.mjpeg.avi \
   -c:v copy -bsf:v mjpeg2jpeg frame_%04d.jpg
person BlackJack    schedule 09.04.2015
comment
Посмотрите на это: code.google.com/p/streaming-video-rtsp-rtp/source/browse/trunk/ - person sudeepdino008; 09.04.2015
comment
Я получил 5-байтовую информацию о длине кадра отсюда. Приведенный выше фрагмент не работает. Кажется, там есть какая-то ненужная информация. Есть ли способ обойти все это и получить кадры? Вы можете использовать: archive.org/download/SecretGardenParty/ как образец mjpeg - person sudeepdino008; 09.04.2015
comment
Я изменил код, чтобы он соответствовал связанному классу Java, и немного расширил пример видео. - person BlackJack; 12.04.2015
comment
Я использую ffmpeg сейчас. Хотя это делает его очень медленным. - person sudeepdino008; 13.04.2015