打印 MJPEG 帧

Printing a MJPEG frame

我正在尝试制作 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()

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

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

以下函数应该遍历由五个 ascii 字符长帧长度计数分隔的 JPEG 图像,例如您移植到 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 class.

的作者编造的

MJPEG 只是一个视频编解码器,它基本上只是 JPEG 图像连接。但它很少以那种“原始”格式出现,而是嵌入到带有元信息的容器格式中,例如 MJPEG 数据、帧速率,可能还有音频等等。

一种这样的格式是 AVI,如您在评论中引用的example MJPEG avi

从这样的文件中提取帧到单个 JPEG 图像比读取带有简单长度信息前缀的 JPEG 图像然后连接到一个文件中要多一些工作。需要实现一个 AVI reader 来充分理解 AVI 格式以获取帧数据。然后是 JPEG reader,它能充分理解 JPEG 格式以读取完整帧,因为它们是背靠背保存的,没有任何长度信息。

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

最后一个“事情”:有不包含完整图像的交错视频,但需要将两个连续的图像合并为一个。每个图像包含每隔一行。你给出的example MJPEG avi就是这样的视频。在不解码、去隔行和重新编码的情况下提取帧时,每个图像的高度仅为视频高度的一半。

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

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