从ffmpeg的标准输出读取的图像有时会变得杂乱无章。为什么?

时间:2014-12-07 10:43:49

标签: java video ffmpeg png

我正在尝试使用ffmpeg将视频转换为图像,然后在Java程序中读取这些图像,将它们转换为文件(仅用于测试,我知道ffmpeg也可以这样做)。这在大多数情况下工作正常,除了一些图像被ffmpeg扰乱,我不知道为什么。我的代码有问题,还是一个ffmpeg怪癖?

ffmpeg有时会在这种奇怪的转换后恢复,而其他时候它会继续产生垃圾。这是一个好的和坏的图像看起来的方式,介于两者之间:Good. Bad.

这是我正在使用的代码。

process = new ProcessBuilder(ffmpeg, "-i", "C:\myvideo.mpeg", "-r", "1", "-f", "image2pipe", "-c", "png", "-").start();

imageStream = process.getInputStream();

BufferedImage imageBuffer = ImageIO.read(imageStream);

while(imageStream.available() > 0)
{
    try
    {
        imageBuffer = ImageIO.read(imageStream);    
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }

    if(imageBuffer != null)
    {
        try
        {
            File outputfile = new File("c:\\image.png");
            ImageIO.write(imageBuffer, "png", outputfile);
        } 
        catch (IOException e)
        {
                e.printStackTrace();
        }
    }
}

我确实得到了一些可能提供一些有用信息的Java堆栈跟踪。它们指的是JPEG,虽然我使用PNG输出。

javax.imageio.IIOException: Unsupported JPEG process: SOF type 0xc7
    at com.sun.imageio.plugins.jpeg.JPEGImageReader.readImageHeader(Native Method)
    at com.sun.imageio.plugins.jpeg.JPEGImageReader.readNativeHeader(Unknown Source)
    at com.sun.imageio.plugins.jpeg.JPEGImageReader.checkTablesOnly(Unknown Source)
    at com.sun.imageio.plugins.jpeg.JPEGImageReader.gotoImage(Unknown Source)
    at com.sun.imageio.plugins.jpeg.JPEGImageReader.readHeader(Unknown Source)
    at com.sun.imageio.plugins.jpeg.JPEGImageReader.readInternal(Unknown Source)
    at com.sun.imageio.plugins.jpeg.JPEGImageReader.read(Unknown Source)
    at javax.imageio.ImageIO.read(Unknown Source)
    at javax.imageio.ImageIO.read(Unknown Source)
    at FilmConverter$1.run(FilmConverter.java:178)

javax.imageio.IIOException: Bogus DQT index 6
    at com.sun.imageio.plugins.jpeg.JPEGImageReader.readImageHeader(Native Method)
    at com.sun.imageio.plugins.jpeg.JPEGImageReader.readNativeHeader(Unknown Source)
    at com.sun.imageio.plugins.jpeg.JPEGImageReader.checkTablesOnly(Unknown Source)
    at com.sun.imageio.plugins.jpeg.JPEGImageReader.gotoImage(Unknown Source)
    at com.sun.imageio.plugins.jpeg.JPEGImageReader.readHeader(Unknown Source)
    at com.sun.imageio.plugins.jpeg.JPEGImageReader.readInternal(Unknown Source)
    at com.sun.imageio.plugins.jpeg.JPEGImageReader.read(Unknown Source)
    at javax.imageio.ImageIO.read(Unknown Source)
    at javax.imageio.ImageIO.read(Unknown Source)
    at FilmConverter$1.run(FilmConverter.java:178)

javax.imageio.IIOException: Unsupported JPEG process: SOF type 0xce
    at com.sun.imageio.plugins.jpeg.JPEGImageReader.readImageHeader(Native Method)
    at com.sun.imageio.plugins.jpeg.JPEGImageReader.readNativeHeader(Unknown Source)
    at com.sun.imageio.plugins.jpeg.JPEGImageReader.checkTablesOnly(Unknown Source)
    at com.sun.imageio.plugins.jpeg.JPEGImageReader.gotoImage(Unknown Source)
    at com.sun.imageio.plugins.jpeg.JPEGImageReader.readHeader(Unknown Source)
    at com.sun.imageio.plugins.jpeg.JPEGImageReader.readInternal(Unknown Source)
    at com.sun.imageio.plugins.jpeg.JPEGImageReader.read(Unknown Source)
    at javax.imageio.ImageIO.read(Unknown Source)
    at javax.imageio.ImageIO.read(Unknown Source)
    at FilmConverter$1.run(FilmConverter.java:178)

java.lang.RuntimeException: New BMP version not implemented yet.
    at com.sun.imageio.plugins.bmp.BMPImageReader.readHeader(Unknown Source)
    at com.sun.imageio.plugins.bmp.BMPImageReader.read(Unknown Source)
    at javax.imageio.ImageIO.read(Unknown Source)
    at javax.imageio.ImageIO.read(Unknown Source)
    at FilmConverter$1.run(FilmConverter.java:178)

1 个答案:

答案 0 :(得分:1)

我遇到了同样的问题并查看了FFmpeg的输出。在实际的PNG数据之间,FFmpeg似乎写入了额外的字节(大多数时间为16字节),包括字符串“END”。我不是PNG文件格式的专家,也许这些字节实际上属于PNG,但是ImageIO不会读取它们。因此,下一个PNG图像的偏移量不正确,并且ImageIO读取器迟早会遇到问题。

有用的讨论是https://stackoverflow.com/a/13777398/518491。它指出ImageIO在一个流中读取多个图像存在问题。它包含了解决此问题的方法,但是没有PNG图像之间提到的额外字节。

我可以通过执行以下操作来解决问题:

  1. 将FFmpeg输出包装到BufferedInputStream
  2. 标记流
  3. 对于每个图像,转到下一个PNG标头的开头,即跳过所有字节,直到找到PNG标头0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A。重置并跳过流,直到它返回到标头的第一个字节(0x89)
  4. 读取图片,如链接帖子(ImageIO.createImageInputStream,ImageReader,...)
  5. 跳过PNG图像的所有字节(getStreamPosition)并再次标记流
  6. 继续第3步