javacv一般通过帧抓取器来进行帧的抓取,随后进行判断,进行相应的处理。下列代码只能处理视频帧中的i帧以及非平面型左右声道在一个buffer中的音频帧(绝大多数都是,包括常见的录音设备)
import java.awt.Image;
import java.nio.ByteBuffer;
import java.nio.ShortBuffer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;
import javax.swing.JFrame;
import org.bytedeco.javacv.CanvasFrame;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameConverter;
public class final_VandA {
public static void main (String[] args) {
//从rtmp服务器拉流 通过nginx的rtmp模块可迅速简单搭建 单纯的rtmp速度也有3s左右的误差 改为通过websocket传输
play("rtmp://192.168.0.1/vod/1.mp4");
//流直播
}
public static void play(String filepath) {
Thread playThread = new Thread(new Runnable() { public void run() {
try {
//开始抓取
@SuppressWarnings("resource")
final FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(filepath);
grabber.start();
//初始化视图
CanvasFrame canvas = new CanvasFrame("云端",CanvasFrame.getDefaultGamma() / grabber.getGamma());//新建一个窗口
canvas.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//初始化扬声器
final AudioFormat audioFormat = new AudioFormat(grabber.getSampleRate(), 16, grabber.getAudioChannels(), true, true);
final DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
final SourceDataLine soundLine = (SourceDataLine) AudioSystem.getLine(info);
soundLine.open(audioFormat);
soundLine.start();
//图像转换器 将帧中的image相关信息提取出来
final Java2DFrameConverter converter = new Java2DFrameConverter();
ExecutorService executor = Executors.newSingleThreadExecutor();
while (!Thread.interrupted()) {
Frame frame = grabber.grab();
if (frame == null) {
break;
}
//帧 为图像帧 或 声音帧
if (frame.image != null) {
Image image = converter.convert(frame);
new Runnable() { public void run() {
canvas.showImage(image);
}}.run();;
} else if (frame.samples != null) {
final ShortBuffer channelSamplesShortBuffer = (ShortBuffer) frame.samples[0];
channelSamplesShortBuffer.rewind();
final ByteBuffer outBuffer = ByteBuffer.allocate(channelSamplesShortBuffer.capacity() * 2);
for (int i = 0; i < channelSamplesShortBuffer.capacity(); i++) {
short val = channelSamplesShortBuffer.get(i);
outBuffer.putShort(val);
}
/*
* 写入到扬声器
* 并准备下一次读取
*/
try {
executor.submit(new Runnable() { public void run() {
soundLine.write(outBuffer.array(), 0, outBuffer.capacity());
outBuffer.clear();
}}).get();
} catch (InterruptedException interruptedException) {
Thread.currentThread().interrupt();
}
}
}
executor.shutdownNow();
executor.awaitTermination(10, TimeUnit.SECONDS);
soundLine.stop();
grabber.stop();
grabber.release();
} catch (Exception exception) {
System.exit(1);
}
}});
playThread.start();
}
}
nginx-rtmp配置
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
rtmp{
server{
listen 1935;
chunk_size 4096;
#点播
application vod {
play /tmp/123/video;#点播的地址 例如 在/tmp/123/video目录下有一个1.mp4 从rtmp://ipaddress/vod/1.mp4拉留即可
}
#直播rtmp推流转为hls协议拉流 相关配置在http
application hls {
live on;
hls on;
hls_path /tmp/123/hls;
hls_fragment 2s;#切片播放的时间长度
hls_playlist_length 10s;#列表时间的长度
hls_continuous on; #连续模式。
hls_cleanup on; #对多余的切片进行删除。
hls_nested on; #嵌套模式
}
#直播 rtmp
application image {
live on;#单纯的rtmp转发桥接 推流和拉流时使用同一个rtmp地址即可 速度最快
}
}
}
http {
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root html;
index index.html index.htm;
}
location /stat {
rtmp_stat all;
rtmp_stat_stylesheet stat.xsl;
}
location /stat.xsl {
root /home/liuyu/下载/nginx-rtmp-module-1.2.1;
}
#hls协议相关转发配置
location /hls { #添加视频流存放地址。
types {
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}
#访问权限开启,否则访问这个地址会报403
autoindex on;
alias /tmp/123/hls;#视频流存放地址
expires -1;
add_header Cache-Control no-cache;
#防止跨域问题
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
}
}
}
}
hls播放实时视频慢,一是协议上的问题,十几秒的误差用hls协议,秒级的误差用rtmp协议,毫秒级的误差用rtsp,二是这个模块的缓存问题,可以尝试降低缓存以及减少推流时的缓存。