bytedeco / javacv

Java interface to OpenCV, FFmpeg, and more
Other
7.56k stars 1.58k forks source link

i hava a question, do you tell me, when i can use javacv, i can ensure direct memory not overflow?? #1807

Open jinl2011 opened 2 years ago

jinl2011 commented 2 years ago

1651587095(1)

when i use javacv, direct memory overflow, linux os kill my process. so i want to know , i can ensure direct memory not overflow??

saudet commented 2 years ago

It looks like your calling the ffmpeg program? That's not under the control of JavaCV. It's something wrong with FFmpeg itself.

jinl2011 commented 2 years ago

yes i calling the ffmpeg for thumbnail

jinl2011 commented 2 years ago

tell me what can i do

saudet commented 2 years ago

It depends, for example, it looks like that can happen with the "split" filter: https://stackoverflow.com/questions/66724106/how-can-i-avoid-an-ffmpeg-out-of-memory-error Are you using the split filter? If not, look for other more similar issues: https://www.google.com/search?client=firefox-b-d&q=ffmpeg+%22killed+process%22

jinl2011 commented 2 years ago

ok i know, but i want my application is stable. so i think, before my code use javacv, my code find the linux os remain memory, if enough code continue, if not enough code throw exception. to protect my application. do you think?

saudet commented 2 years ago

Since the ffmpeg program runs in another process, it cannot crash your Java application. It's already doing what you need it to do.

jinl2011 commented 2 years ago

i use java, pom.xml dependency javacv1.5.7 the same process.

saudet commented 2 years ago

Then you should use the ffmpeg program, that's fine. What's the question? If you expect an answer, you'll need to ask a question.

jinl2011 commented 2 years ago

how-can-i-avoid-an-ffmpeg-out-of-memory-error in my jvm process ??

saudet commented 2 years ago

Like I keep telling you, you'll either need to fix whatever is wrong with the code of ffmpeg, or you'll need to run the bad code as part of another process, for example, using the ffmpeg program. There is no other option! Please explain what you do not understand.

jinl2011 commented 2 years ago

in my jvm process, when i use javacv( javacv use ffmpeg), jvm direct memory is bigger and bigger, in the end, jvm was killed by linux os.

saudet commented 2 years ago

Like I keep telling you, if you want someone to look at your code for bugs, you'll need to at least provide the code. I cannot tell you what is wrong with your code if I cannot see your code. Do you understand?

jinl2011 commented 2 years ago

yes, i know you tell, but i hava to use same process.

jinl2011 commented 2 years ago

can you speak chiness??

jinl2011 commented 2 years ago

我明白你说的意思,但是,我目前必须要在一个进程内去使用javacv

jinl2011 commented 2 years ago

import it.sauronsoftware.jave.Encoder; import it.sauronsoftware.jave.MultimediaInfo; import it.sauronsoftware.jave.Encoder; import it.sauronsoftware.jave.MultimediaInfo; import lombok.Cleanup; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.bytedeco.javacv.FFmpegFrameGrabber; import org.bytedeco.javacv.Frame; import org.bytedeco.javacv.Java2DFrameConverter; import org.bytedeco.javacv.OpenCVFrameConverter; import org.bytedeco.opencv.global.opencv_core; import org.bytedeco.opencv.opencv_core.IplImage; import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO; import javax.imageio.stream.ImageOutputStream; import java.awt.image.BufferedImage; import java.io.*; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List;

@Component @Slf4j @RequiredArgsConstructor public class VideoImageUtils {

/**
 * 获取指定视频的帧并保存为图片至指定目录
 * @param inputStream  源视频文件输入流
 * @throws Exception
 * @throws IOException
 */
public static byte[] fetchFrame(InputStream inputStream) throws Exception, IOException {
    if (!(inputStream instanceof FileInputStream)) {
        log.info("不支持的流类型");
        return null;
    }
    Field pathField = inputStream.getClass().getDeclaredField("path");
    if (!pathField.isAccessible()) {
        pathField.setAccessible(true);
    }
    String tmpFilePath = pathField.get(inputStream).toString();
    //重新构建一个File对象,FFmpegFrameGrabber的构造函数使用FileInputStream作为参数会报错
    File tmpFile = new File(tmpFilePath);
    @Cleanup
    FFmpegFrameGrabber ff = new FFmpegFrameGrabber(tmpFile);
    ff.start();
    ff.getAudioChannels();
    //视频的旋转角度
    String rotate = ff.getVideoMetadata("rotate");
    int length = ff.getLengthInFrames();
    int i = 0;
    Frame f;
    while (i < length) {
        f = ff.grabFrame();
        if (i > 500) {
            break;
        }
        if (f.image != null) {
            IplImage src = null;
            if(null != rotate && rotate.length() > 1) {
                @Cleanup
                OpenCVFrameConverter.ToIplImage converter = new OpenCVFrameConverter.ToIplImage();
                src = converter.convert(f);
                f = converter.convert(rotate(src, Integer.parseInt(rotate)));
            }
            if (src != null) {
                src.close();
                src = null;
            }
            return doExecuteFrame(f);
        }
        i++;
    }
    ff.stop();
    ff = null;
    return null;
}
public static byte[] fetchFrame(String filePath) throws Exception, IOException {
    File tmpFile = new File(filePath);
    @Cleanup
    FFmpegFrameGrabber ff = new FFmpegFrameGrabber(tmpFile);
    ff.start();
    ff.getAudioChannels();
    //视频的旋转角度
    String rotate = ff.getVideoMetadata("rotate");
    int length = ff.getLengthInFrames();
    int i = 0;
    Frame f;
    while (i < length) {
        f = ff.grabFrame();
        if (i > 500) {
            break;
        }
        if (f.image != null) {
            IplImage src = null;
            if(null != rotate && rotate.length() > 1) {
                @Cleanup
                OpenCVFrameConverter.ToIplImage converter = new OpenCVFrameConverter.ToIplImage();
                src = converter.convert(f);
                f = converter.convert(rotate(src, Integer.parseInt(rotate)));
            }
            if (src != null) {
                src.close();
                src = null;
            }
            return doExecuteFrame(f);
        }
        i++;
    }
    ff.stop();
    ff = null;
    return null;
}
public static void main(String[] args) throws Exception{
    for (int i =0; i<10000;i++){
        String filePath = "D:\\迅雷下载\\";

// test - 副本 (15) filePath += "test - 副本 ("+(i%19)+").mp4"; byte[] bytes = fetchFrame(filePath); byteToFile(bytes, "D:\迅雷下载\image\test"+i+".png"); System.out.println("==============================================="+i); // Thread.sleep(5000); // if (i%20==0){ // System.gc(); // } } }

public static void byteToFile(byte[] contents, String filePath) throws Exception{
    BufferedInputStream bis = null;
    FileOutputStream fos = null;
    BufferedOutputStream output = null;
    ByteArrayInputStream byteInputStream = null;
    try {
        byteInputStream = new ByteArrayInputStream(contents);
        bis = new BufferedInputStream(byteInputStream);
        File file = new File(filePath);
        // 获取文件的父路径字符串
        File path = file.getParentFile();
        if (!path.exists()) {

            boolean isCreated = path.mkdirs();
            if (!isCreated) {
                System.out.println("==========> create Fail");
            }
        }
        fos = new FileOutputStream(file);
        // 实例化OutputString 对象
        output = new BufferedOutputStream(fos);
        byte[] buffer = new byte[1024];
        int length = bis.read(buffer);
        while (length != -1) {
            output.write(buffer, 0, length);
            length = bis.read(buffer);
        }
        output.flush();
    } catch (Exception e) {
        System.out.println("==========> create Fail");
        throw e;
    } finally {
        try {
            byteInputStream.close();
            bis.close();
            fos.close();
            output.close();
        } catch (IOException e) {
            System.out.println("==========> create Fail");
            throw e;
        }
    }
}

public static IplImage rotate(IplImage src, int angle) {
    IplImage img = IplImage.create(src.height(),src.width(),src.depth(),src.nChannels());

    opencv_core.cvTranspose(src,img);
    opencv_core.cvFlip(img,img,angle);
    return img;
}

public static byte [] doExecuteFrame(Frame f) {
    if (null == f || null == f.image) {
        return null;
    }
    @Cleanup
    Java2DFrameConverter converter = new Java2DFrameConverter();
    BufferedImage bufferedImage = converter.getBufferedImage(f);
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    try {
        ImageIO.write(bufferedImage,"png", byteArrayOutputStream);
    } catch (IOException e) {
        log.error("视频缩略图生成失败", e);
        byteArrayOutputStream = null;
    } finally {
        if (bufferedImage != null) {
            bufferedImage.flush();
            bufferedImage.getGraphics().dispose();
        }
        f.close();
        f = null;
        converter = null;
    }
    if (byteArrayOutputStream == null) {
        return null;
    }
    return byteArrayOutputStream.toByteArray();
}

}

saudet commented 2 years ago

You're not calling converter.close() anywhere, that can leak memory.

jinl2011 commented 2 years ago

image

jinl2011 commented 2 years ago

My fundamental desire is to control how the JVM processes use out-of-heap memory.

saudet commented 2 years ago

If you're talking about a tool like JavaCPP, that's explained here: http://bytedeco.org/news/2018/07/17/bytedeco-as-distribution/