随笔 - 59,  文章 - 1,  评论 - 0,  阅读 - 24152

需要进行简单的音视频编程,如果不是特别熟悉C/C++,那么JavaCV应该是比较好的选择,下面记录一下JavaCV 采集摄像头和麦克风数据推送RTMP流的方法。

同时采集视频和音频需要最好采用不同的线程进行。

1. 视频采集

视频采集使用OpenCVFrameGrabber(当然也可以使用FFmpegFrameGrabber):

public class VideoRecorder implements Runnable {
private static final int VIDEO_DEVICE_INDEX = 0;
private FFmpegFrameRecorder recorder;
private int width, height;
public VideoRecorder(FFmpegFrameRecorder recorder, int width, int height) {
this.recorder = recorder;
this.width = width;
this.height = height;
}
@Override
public void run() {
try {
OpenCVFrameGrabber grabber = new OpenCVFrameGrabber(VIDEO_DEVICE_INDEX);
grabber.setImageWidth(width);
grabber.setImageHeight(height);
grabber.start();
long startTS = 0, videoTS = 0;
Frame frame = null;
while (!Thread.interrupted() && (frame = grabber.grab()) != null) {
if (startTS == 0) {
startTS = System.currentTimeMillis();
}
videoTS = 1000 * (System.currentTimeMillis() - startTS);
if (videoTS > recorder.getTimestamp()) {
recorder.setTimestamp(videoTS);
}
recorder.record(frame);
}
grabber.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}

这里的recorder(FFmpegFrameRecorder)是推流使用的,即OpenCVFrameGrabber采集的视频流推送到recorder中,具体在下面的rtmp推流中说明。

2. 音频采集

音频采集直接采用Java的API,读取麦克风的数据:

public class AudioRecoder implements Runnable {
private FFmpegFrameRecorder recorder;
private int channels;
private int sampleRate;
public AudioRecoder(FFmpegFrameRecorder recorder, int sampleRate, int channels) {
this.recorder = recorder;
this.sampleRate = sampleRate;
this.channels = channels;
}
@Override
public void run() {
try {
AudioFormat format = new AudioFormat(Float.valueOf(sampleRate), 16, channels, true, false);
TargetDataLine line = (TargetDataLine) AudioSystem.getLine(new DataLine.Info(TargetDataLine.class, format));
line.open(format);
line.start();
int sampleRate = (int) format.getSampleRate();
int numChannels = format.getChannels();
byte[] buffer = new byte[sampleRate * numChannels];
while (!Thread.interrupted()) {
int nBytesRead = 0;
while (nBytesRead == 0) {
nBytesRead = line.read(buffer, 0, line.available());
}
int nSamplesRead = nBytesRead / 2;
short[] samples = new short[nSamplesRead];
ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(samples);
ShortBuffer sBuff = ShortBuffer.wrap(samples, 0, nSamplesRead);
recorder.recordSamples(sampleRate, numChannels, sBuff);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

需要注意的是,指定的位数是16,所以需要将采集的byte转化为short(小端模式),同样的,这里的recorder(FFmpegFrameRecorder)是推流使用的。

3. rtmp推流

rtmp推流采用FFmpegFrameRecorder,format设置为FLV,视频编码采用H264,音频编码采用AAC。

public class Recoder {
public static void main(String[] args) throws Exception {
String rtmpURI = "rtmp://127.0.0.1:1935/app/test";
int width = 1280, height = 720, sampleRate = 44100, channels = 2;
FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(rtmpURI, width, height, channels);
// Video
recorder.setInterleaved(true);
recorder.setVideoOption("tune", "zerolatency");
recorder.setVideoOption("preset", "ultrafast");
recorder.setVideoOption("crf", "28");
recorder.setVideoBitrate(2000000);
// H.264
recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
recorder.setFormat("flv");
recorder.setFrameRate(25);
recorder.setGopSize(25 * 2);
// Audio
recorder.setAudioOption("crf", "0");
recorder.setAudioQuality(0);
recorder.setAudioBitrate(192000);
recorder.setSampleRate(sampleRate);
recorder.setAudioChannels(channels);
// AAC
recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
recorder.start();
Thread vt = new Thread(new VideoRecorder(recorder, width, height));
Thread at = new Thread(new AudioRecoder(recorder, sampleRate, channels));
vt.start();
at.start();
vt.join();
at.join();
recorder.close();
}
}

这里需要注意的是,启动音视频采集线程后,需要将线程加入到主线程中:

vt.join();
at.join()

4. 效果预览

可以使用VLC拉流查看recorder的推流效果:

注意rtmp服务器需要自己搭建。

posted on   $$X$$  阅读(1614)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)

点击右上角即可分享
微信分享提示