JavaCV中FFmpegFrameGrabber調用start()方法時出現阻塞的解決辦法

JvaCV中FFmpegFrameGrabber調用start方法阻塞的解決辦法

項目碼雲(Gitee)地址:https://gitee.com/banmajio/RTSPtoRTMP
項目github地址:https://github.com/banmajio/RTSPtoRTMP
個人博客:banmajio’s blog

javacv使用ffmpeg將rtsp轉rtmp直播流播放的問題解決與優化系列文章:
FFmpeg轉封裝rtsp到rtmp(無需轉碼,低資源消耗)
JavaCV中FFmpegFrameGrabber調用start()方法時出現阻塞的解決辦法
JavaCV使用FFmpeg進行rtsp轉rtmp直播流畫面延時的優化方法
JavaCV1.5.3版本FFmpegFrameGrabber初始化的時候加載時間長的解決方法
av_write_frame() error -22 while writing video packet解決方法


問題描述

目前出現阻塞的情況有如下兩種:
1.拉歷史流的時候,會發生阻塞,grabber.start()阻塞無法繼續執行。
2.如果rtsp指令的ip亂輸(或者無法建立連接),start()也會發生阻塞。

解決方法


問題1

可以通過設置超時時間,如果拉不到流,觸發超時時間,自動斷開TCP連接。

		// 設置採集器構造超時時間(單位微秒,1秒=1000000微秒)
		grabber.setOption("stimeout", "2000000");

上述方法貌似沒多大作用,依然會被阻塞…

查看源碼發現會發生阻塞的函數有兩個:

1.avformat_open_input():

打開流通道,探測一些視頻格式等信息,對AVFormatContext結構體初始化。
對於這個函數阻塞的優化方法:將下面函數的seekCallback 設置爲null,禁止javacv查找。

		avio = avio_alloc_context(new BytePointer(av_malloc(4096)), 4096, 0, oc, readCallback, null,
					maximumSize > 0 ? seekCallback : null);

2.avformat_find_stream_info():

探測流信息(寬高碼率等信息)
這個函數存在執行時間較長或者阻塞的問題,可以通過以下屬性設置,來減小函數執行的時間。probesize屬性限制探測時讀取的最大數據量。max_analyze_duration屬性限制info函數執行的時長,AV_TIME_BASE是單位秒。但是對於阻塞的問題好像並不能有效的解決,只是可以縮短函數的執行時間:

		// 限制avformat_find_stream_info接口內部讀取的最大數據量
		oc.probesize(5120);
		// 設置avformat_find_stream_info這個函數的持續時長,超過這個時間不結束也會結束
		oc.max_analyze_duration(5 * AV_TIME_BASE);
		// 將avformat_find_stream_info內部讀取的數據包不放入AVFormatContext的緩衝區packet_buffer中
		oc.flags(AVFormatContext.AVFMT_FLAG_NOBUFFER);

3.使用inputstream進行推流時,最新版本的javacv(1.5.3),在grabber new的時候有一行註釋:

/**
	 * Calls {@code FFmpegFrameGrabber(inputStream, Integer.MAX_VALUE - 8)} so that
	 * the whole input stream is seekable.
	 */
	public FFmpegFrameGrabber(InputStream inputStream) {
		this(inputStream, Integer.MAX_VALUE - 8);
	}

	/** Set maximumSize to 0 to disable seek and minimize startup time. */
	//將maximumSize設置爲0以禁用查找並最小化啓動時間
	public FFmpegFrameGrabber(InputStream inputStream, int maximumSize) {
		this.inputStream = inputStream;
		this.closeInputStream = true;
		this.pixelFormat = AV_PIX_FMT_NONE;
		this.sampleFormat = AV_SAMPLE_FMT_NONE;
		this.maximumSize = maximumSize;
	}

將maximumSize設置爲0以禁用查找並最小化啓動時間;也就是grabber = new FFmpegFrameGrabber(inputStream,0);效果等同於上述序號1的設置,不修改源碼來禁用avio_alloc_context()函數的seekCallback

問題2

上述設置超時時間的方法對於拉流地址(rtsp指令中的ip)輸入錯誤時並不生效,依舊會阻塞,查看源碼奈何才疏學淺不知道如何解決。變向通過檢測是否能建立TCP連接,來判定是否可以正常推拉流。

如下代碼:如果可以建立連接,則繼續執行;否則釋放資源,return null;


		// 解決ip輸入錯誤時,grabber.start();出現阻塞無法釋放grabber而導致後續推流無法進行;
		Socket rtspSocket = new Socket();
		Socket rtmpSocket = new Socket();
		// 建立TCP Scoket連接,超時時間1s,如果成功繼續執行,否則return
		try {
			rtspSocket.connect(new InetSocketAddress(cameraPojo.getIp(), 554), 1000);
		} catch (IOException e) {
			grabber.stop();
			grabber.close();
			rtspSocket.close();
			System.err.println("與拉流地址建立連接失敗...");
			return null;
		}

		try {
			rtmpSocket.connect(new InetSocketAddress(IpUtil.IpConvert(config.getPush_ip()),
					Integer.parseInt(config.getPush_port())), 1000);
		} catch (IOException e) {
			grabber.stop();
			grabber.close();
			rtspSocket.close();
			System.err.println("與推流地址建立連接失敗...");
			return null;
		}

如有錯誤或者更好的解決辦法,請指正!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章