bytedeco/javacv

Memory leak found in FFmpegFrameRecorder

Open

#2.356 aberto em 23 de set. de 2025

Ver no GitHub
 (18 comments) (0 reactions) (0 assignees)Java (1.583 forks)batch import
bughelp wantedquestion

Métricas do repositório

Stars
 (6.985 stars)
Métricas de merge de PR
 (Nenhuma PRs mesclada em 30d)

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();
    }
}

}

Guia do colaborador