打印MJPEG框架

时间:2015-04-09 10:30:50

标签: python file video bit-manipulation 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()

1 个答案:

答案 0 :(得分:2)

代码中的长度实际上只是第五个字节的值。将所有其他加数向左移动至少8位,然后屏蔽除8位最低位之外的所有位。由于前面的移位操作,这些位都为零。

可以实现的一个简单的附加测试是,如果帧数据以JPEG开始的图像标记(FF D8)开始,并以图像结束标记(DD D9)结束。

以下函数应该迭代JPEG图像,这些图像由五个ascii字符长帧长度计数分隔,就像您移植到Python的VideoStream.java一样:

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,就像您在评论中引用的example MJPEG avi一样。

将这样的文件中的帧提取为单个JPEG图像比读取前缀为简单长度信息的JPEG图像然后在一个文件中连接起来要多一些工作。人们需要实现一个AVI阅读器,它能够充分理解AVI格式以获得帧数据。然后是一个JPEG读取器,它能够理解足够的JPEG格式来读取整个帧,因为它们是在没有任何长度信息的情况下背靠背保存的。

下一个问题是并非所有MJPEG都包含可用作独立JPEG图像的帧。有些人缺少解压缩图像数据所需的数据表(霍夫曼表)。 MJPEG编解码器的AVI规范中有一个固定的表。该表由软件用于解码,并且在保存为JPEG文件时必须将其注入帧中。

最后一个“事物”:隔行扫描视频不包含完整图像,但需要将两个连续图像组合成一个。每个图像包含每隔一行。您给定的example MJPEG avi 这样的视频。在不解码,逐行扫描和重新编码的情况下提取帧时,每个图像只有视频高度的一半。

为了更好地了解单个图像的外观,ffmpeg命令行提取帧数据并注入丢失的数据表,以获得独立的JPEG图像:

ffmpeg -i bowlerhatdancer.sleepytom.SGP.mjpeg.avi \
   -c:v copy -bsf:v mjpeg2jpeg frame_%04d.jpg