javacv实现拉流播放

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,二是这个模块的缓存问题,可以尝试降低缓存以及减少推流时的缓存。

发布了6 篇原创文章 · 获赞 0 · 访问量 524

猜你喜欢

转载自blog.csdn.net/qq_42873492/article/details/104214476
今日推荐