bytedeco/javacv

Memory leak found in FFmpegFrameRecorder

Open

#2356 opened on Sep 23, 2025

View on GitHub
 (18 comments) (0 reactions) (0 assignees)Java (6,985 stars) (1,583 forks)batch import
bughelp wantedquestion

Description

Dear author, I met a native off-heap memory leak problem when I try to convert pcm audio to mp3 format, and javacv/javacpp used is 1.5.9 version, ffmpeg is 6.0-1.5.9. Following is the code, and even if I delete the sentences: new FFmpegFrameRecorder/start (recorder)/stop (recorder) and the internal packet capture while loop, only keep the sentences: start (grabber) and stop (grabber), there is still a memory leak, please help to check, thank you very much!

import org.apache.commons.io.IOUtils; import org.bytedeco.ffmpeg.global.avcodec; import org.bytedeco.javacv.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Paths; import java.util.concurrent.CountDownLatch;

public class Test { private static final Logger LOGGER = LoggerFactory.getLogger(Test.class);

private static boolean start(FrameGrabber grabber) {
    try {
        grabber.start();
        return true;
    } catch (org.bytedeco.javacv.FrameGrabber.Exception e2) {
        e2.printStackTrace();
        try {
            LOGGER.error("首次打开抓取器失败,准备重启抓取器...");
            grabber.restart();
            return true;
        } catch (org.bytedeco.javacv.FrameGrabber.Exception e) {
            try {
                LOGGER.error("重启抓取器失败,正在关闭抓取器...", e);
                grabber.stop();
            } catch (org.bytedeco.javacv.FrameGrabber.Exception e1) {
                LOGGER.error("停止抓取器失败!", e1);
            }
        }

    }
    return false;
}

private static boolean start(FrameRecorder recorder) {
    try {
        recorder.start();
        return true;
    } catch (Exception e2) {
        try {
            LOGGER.error("首次打开录制器失败!准备重启录制器...", e2);
            recorder.stop();
            recorder.start();
            return true;
        } catch (Exception e) {
            try {
                LOGGER.error("重启录制器失败!正在停止录制器...", e);
                recorder.stop();
            } catch (Exception e1) {
                LOGGER.error("关闭录制器失败!", e);
            }
        }
    }
    return false;
}

private static boolean stop(FrameGrabber grabber) {
    try {
        grabber.flush();
        return true;
    } catch (org.bytedeco.javacv.FrameGrabber.Exception e) {
        return false;
    } finally {
        try {
            grabber.stop();
            grabber.release();
        } catch (org.bytedeco.javacv.FrameGrabber.Exception e) {
            LOGGER.error("关闭抓取器失败", e);
        }
    }
}

private static boolean stop(FrameRecorder recorder) {
    try {
        recorder.flush();
        return true;
    } catch (Exception e) {
        return false;
    } finally {
        try {
            recorder.stop();
            recorder.release();
        } catch (Exception e) {

        }
    }
}

public static byte[] convert2Bytes(InputStream inputStream,
                                          String fromType, String toType, String sample_rate) {
    FFmpegFrameRecorder recorder = null;
    FFmpegFrameGrabber grabber = null;
    Frame audioSamples = null;
    ByteArrayOutputStream swapStream = null;
    try {
        grabber = new FFmpegFrameGrabber(inputStream);
        if ("pcm".equals(fromType)) {
            grabber.setAudioCodec(avcodec.AV_CODEC_ID_PCM_S16LE);
            grabber.setFormat("s16le");
        } else if ("mp3".equals(fromType)) {
            grabber.setAudioCodec(avcodec.AV_CODEC_ID_MP3);
            grabber.setFormat("mp3");
        } else if ("wav".equals(fromType)) {
            grabber.setAudioCodec(avcodec.AV_CODEC_ID_ADPCM_IMA_WAV);
            grabber.setFormat("wav");
        } else if ("opus".equals(fromType)) {
            grabber.setAudioCodec(avcodec.AV_CODEC_ID_MP2);
            grabber.setFormat("ogg");
        }

        int sampleRate = Integer.valueOf(sample_rate);
        grabber.setSampleRate(sampleRate);
        if (start(grabber)) {
            swapStream = new ByteArrayOutputStream();
            recorder = new FFmpegFrameRecorder(swapStream, 1);
            recorder.setAudioOption("crf", "0");
            recorder.setAudioBitrate(16000);
            recorder.setAudioChannels(1);
            recorder.setSampleRate(sampleRate);
            recorder.setAudioQuality(0);
            if ("mp3".equals(toType)) {
                recorder.setFormat("mp3");
            }
            if ("wav".equals(toType)) {
                recorder.setFormat("wav");
            }
            if ("pcm".equals(toType) || "raw".equals(toType)) {
                recorder.setFormat("s16le");
            }

            recorder.setAudioOption("aq", "10");
            // 开启录制器
            if (start(recorder)) {
                try {
                    while ((audioSamples = grabber.grab()) != null) {
                        recorder.setTimestamp(grabber.getTimestamp());
                        recorder.record(audioSamples);
                        audioSamples.close();
                    }
                } catch (org.bytedeco.javacv.FrameGrabber.Exception e1) {
                    LOGGER.error("抓取失败", e1);
                } catch (Exception e) {
                    LOGGER.error("录制失败", e);
                }
            }
        }
    } finally {
        if (grabber != null) {
            try {
                stop(grabber);
            } catch (Exception e) {
                LOGGER.error("grabber stop error", e);
            }
        }
        if (recorder != null) {
            try {
                stop(recorder);
            } catch (Exception e) {
                LOGGER.error("recorder stop error", e);
            }
        }
    }
    byte[] result = swapStream.toByteArray();
    IOUtils.closeQuietly(inputStream);
    IOUtils.closeQuietly(swapStream);
    return result;
}

public static void main(String[] args) throws Exception {
    System.setProperty("org.bytedeco.javacpp.logger.debug", "true");

    String inputPcmFile = "C:\\audios\\test_16000.pcm";
    int retries = 1;
    int ncp = 1;
    if(args != null && args.length == 3) {
        inputPcmFile = args[0];
        ncp = Integer.parseInt(args[1]);
        retries = Integer.parseInt(args[2]);
    }

    final CountDownLatch cdl = new CountDownLatch(ncp);
    String finalInputFile = inputPcmFile;

    byte[] pcmBytes = Files.readAllBytes(Paths.get(finalInputFile));
    int finalRetries = retries;
    for(int i = 0; i < ncp; i++) {
        Thread thread = new Thread(() -> {
            for (int j = 0; j < finalRetries; j++) {
                try {
                    ByteArrayInputStream resultInputStream = new ByteArrayInputStream(pcmBytes);
                    byte[] mp3Bytes = convert2Bytes(resultInputStream, "pcm", "mp3", "16000");
                } catch (Exception e) {
                    System.err.println("exception occurs: " + e.getClass().getCanonicalName());
                }
            }
            cdl.countDown();
        });
        thread.start();
    }

    try {
        cdl.await();//需要捕获异常,当其中线程数为0时这里才会继续运行
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

}

Contributor guide