JavaCV FFmpegFrameGrabber 预加载音频
JavaCV FFmpegFrameGrabber preload audio
我有一个使用 javacv 的 FFmpegFrameRecorder 将视频数据流式传输到 RTMP 服务器的应用程序。我想从一个单独的文件中向此流添加一些音频 - 我想重复播放的一个简短的声音片段。
鉴于声音片段很短,我想将音频数据预加载到内存中并循环播放 - 这样我就可以避免过多的 IO 等。
我已尝试使用 javacv 的 FFmpegFrameGrabber 将音频添加到流中,如多个教程中所述。
如果我不尝试 preload/cache 任何音频数据,添加音频效果很好,例如:
private FFmpegFrameRecorder frameRecorder;
private FFmpegFrameGrabber frameGrabber;
...
//frameRecorder and frameGrabber setup during initialization
...
public void record(IplImage image) {
try {
frameRecorder.record(image);
Frame frame = frameGrabber.grabFrame();
if(frame == null) {
frameGrabber = new FFmpegFrameGrabber("audioFileHere.wav");
frameGrabber.start();
frame = frameGrabber.grabFrame();
}
frameRecorder.record(frame);
} catch (FrameRecorder.Exception e) {
log.error(getMarker(FATAL), "Can't record frame!", e);
} catch (FrameGrabber.Exception e) {
log.error(getMarker(FATAL), "Can't record frame!", e);
}
}
但是,如果我尝试预加载音频数据,我会听到正在播放的垃圾声音:
private FFmpegFrameRecorder frameRecorder;
private List<FrameData> audioData;
private static final class FrameData {
public final Buffer[] samples;
public final Integer sampleRate;
public final Integer audioChannels;
//Constructors, getters and setters here
}
...
//frameRecorder setup during initialization
audioData = new ArrayList<>();
FFmpegFrameGrabber audioGrabber = new FFmpegFrameGrabber("audioFileHere.wav");
try {
audioGrabber.start();
Frame frame;
while ((frame = audioGrabber.grabFrame()) != null) {
Buffer[] buffers = frame.samples;
Buffer[] copiedBuffers = new Buffer[buffers.length];
for (int i = 0; i < buffers.length; i++) {
copiedBuffers[i] = ((ShortBuffer) buffers[i]).duplicate();
}
FrameData frameData = new FrameData(copiedBuffers, frame.sampleRate, frame.audioChannels);
audioData.add(frameData);
}
} catch (FrameGrabber.Exception e) {
e.printStackTrace();
}
...
private int frameCount = 0;
public void record(IplImage image) {
frameCount++;
try {
FrameData frameData = audioData.get(frameCount % audioData.size());
frameRecorder.record(image);
frameRecorder.record(frameData.sampleRate, frameData.audioChannels, frameData.samples);
} catch (FrameRecorder.Exception e) {
log.error(getMarker(FATAL), "Can't record frame!", e);
}
}
注意:我必须深度复制 Frame 对象,因为 FFmpegFrameGrabber.grabFrame() 回收单个 Frame 对象
谁能解释为什么这不起作用and/or我怎样才能达到预期的结果?
我在我的特定代码示例中找到了解决方案。 ShortBuffer.duplicate() 不进行深层复制,因此导致了问题。相反,我必须创建一个新的 ShortBuffer 并复制内容。
我有一个使用 javacv 的 FFmpegFrameRecorder 将视频数据流式传输到 RTMP 服务器的应用程序。我想从一个单独的文件中向此流添加一些音频 - 我想重复播放的一个简短的声音片段。
鉴于声音片段很短,我想将音频数据预加载到内存中并循环播放 - 这样我就可以避免过多的 IO 等。
我已尝试使用 javacv 的 FFmpegFrameGrabber 将音频添加到流中,如多个教程中所述。
如果我不尝试 preload/cache 任何音频数据,添加音频效果很好,例如:
private FFmpegFrameRecorder frameRecorder;
private FFmpegFrameGrabber frameGrabber;
...
//frameRecorder and frameGrabber setup during initialization
...
public void record(IplImage image) {
try {
frameRecorder.record(image);
Frame frame = frameGrabber.grabFrame();
if(frame == null) {
frameGrabber = new FFmpegFrameGrabber("audioFileHere.wav");
frameGrabber.start();
frame = frameGrabber.grabFrame();
}
frameRecorder.record(frame);
} catch (FrameRecorder.Exception e) {
log.error(getMarker(FATAL), "Can't record frame!", e);
} catch (FrameGrabber.Exception e) {
log.error(getMarker(FATAL), "Can't record frame!", e);
}
}
但是,如果我尝试预加载音频数据,我会听到正在播放的垃圾声音:
private FFmpegFrameRecorder frameRecorder;
private List<FrameData> audioData;
private static final class FrameData {
public final Buffer[] samples;
public final Integer sampleRate;
public final Integer audioChannels;
//Constructors, getters and setters here
}
...
//frameRecorder setup during initialization
audioData = new ArrayList<>();
FFmpegFrameGrabber audioGrabber = new FFmpegFrameGrabber("audioFileHere.wav");
try {
audioGrabber.start();
Frame frame;
while ((frame = audioGrabber.grabFrame()) != null) {
Buffer[] buffers = frame.samples;
Buffer[] copiedBuffers = new Buffer[buffers.length];
for (int i = 0; i < buffers.length; i++) {
copiedBuffers[i] = ((ShortBuffer) buffers[i]).duplicate();
}
FrameData frameData = new FrameData(copiedBuffers, frame.sampleRate, frame.audioChannels);
audioData.add(frameData);
}
} catch (FrameGrabber.Exception e) {
e.printStackTrace();
}
...
private int frameCount = 0;
public void record(IplImage image) {
frameCount++;
try {
FrameData frameData = audioData.get(frameCount % audioData.size());
frameRecorder.record(image);
frameRecorder.record(frameData.sampleRate, frameData.audioChannels, frameData.samples);
} catch (FrameRecorder.Exception e) {
log.error(getMarker(FATAL), "Can't record frame!", e);
}
}
注意:我必须深度复制 Frame 对象,因为 FFmpegFrameGrabber.grabFrame() 回收单个 Frame 对象
谁能解释为什么这不起作用and/or我怎样才能达到预期的结果?
我在我的特定代码示例中找到了解决方案。 ShortBuffer.duplicate() 不进行深层复制,因此导致了问题。相反,我必须创建一个新的 ShortBuffer 并复制内容。