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