時間:2023-10-09 09:54:01 | 來源:網(wǎng)站運營
時間:2023-10-09 09:54:01 來源:網(wǎng)站運營
直播視頻網(wǎng)站設計到實現(xiàn)(例子-java版本):首先這里直播使用到的流程為:docker run -p 11935:1935 -p 1985:1985 -p 8080:8080 ossrs/srs:3
第二步maven中添加依賴 <!-- javacv相關依賴,一個就夠了 --> <dependency> <groupId>org.bytedeco</groupId> <artifactId>javacv-platform</artifactId> <version>1.5.8</version> </dependency>
package com.netsdk.test;import org.bytedeco.ffmpeg.avcodec.AVCodecParameters;import org.bytedeco.ffmpeg.avformat.AVFormatContext;import org.bytedeco.ffmpeg.avformat.AVStream;import org.bytedeco.ffmpeg.global.avcodec;import org.bytedeco.ffmpeg.global.avutil;import org.bytedeco.javacv.*;import java.util.logging.Logger;public class Test1 { /** * 本地MP4文件的完整路徑 */ private static final String MP4_FILE_PATH = "E://data//testh264.mp4"; /** * SRS的推流地址這里要改 */ private static final String SRS_PUSH_ADDRESS = "rtmp://xxxxxx:11935/live/457578"; /** * 讀取指定的mp4文件,推送到SRS服務器 * @param sourceFilePath 視頻文件的絕對路徑 * @param PUSH_ADDRESS 推流地址 * @throws Exception */ private static void grabAndPush(String sourceFilePath, String PUSH_ADDRESS) throws Exception { // ffmepg日志級別 avutil.av_log_set_level(avutil.AV_LOG_INFO); FFmpegLogCallback.set(); // 實例化幀抓取器對象,將文件路徑傳入 FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(MP4_FILE_PATH); long startTime = System.currentTimeMillis(); System.out.println("開始初始化幀抓取器"); // 初始化幀抓取器,例如數(shù)據(jù)結(jié)構(gòu)(時間戳、編碼器上下文、幀對象等), // 如果入?yún)⒌扔趖rue,還會調(diào)用avformat_find_stream_info方法獲取流的信息,放入AVFormatContext類型的成員變量oc中 grabber.start(true); System.out.println("幀抓取器初始化完成,耗時[{}]毫秒:"); System.out.println(System.currentTimeMillis()-startTime); // grabber.start方法中,初始化的解碼器信息存在放在grabber的成員變量oc中 AVFormatContext avFormatContext = grabber.getFormatContext(); // 文件內(nèi)有幾個媒體流(一般是視頻流+音頻流) int streamNum = avFormatContext.nb_streams(); // 沒有媒體流就不用繼續(xù)了 if (streamNum<1) { System.out.println("文件內(nèi)不存在媒體流"); return; } // 取得視頻的幀率 int frameRate = (int)grabber.getVideoFrameRate(); System.out.println("視頻幀率[{}],視頻時長[{}]秒,媒體流數(shù)量[{}]"); System.out.println(frameRate); System.out.println(avFormatContext.duration()/1000000); System.out.println(avFormatContext.nb_streams()); // 遍歷每一個流,檢查其類型 for (int i=0; i< streamNum; i++) { AVStream avStream = avFormatContext.streams(i); AVCodecParameters avCodecParameters = avStream.codecpar(); System.out.println("流的索引[{}],編碼器類型[{}],編碼器ID[{}]"); System.out.println(i); System.out.println(avCodecParameters.codec_type()); System.out.println(avCodecParameters.codec_id()); } // 視頻寬度 int frameWidth = grabber.getImageWidth(); // 視頻高度 int frameHeight = grabber.getImageHeight(); // 音頻通道數(shù)量 int audioChannels = grabber.getAudioChannels(); System.out.println("視頻寬度[{}],視頻高度[{}],音頻通道數(shù)[{}]"); System.out.println(frameWidth); System.out.println(frameHeight); System.out.println(audioChannels); // 實例化FFmpegFrameRecorder,將SRS的推送地址傳入 FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(SRS_PUSH_ADDRESS, frameWidth, frameHeight, audioChannels); // 設置編碼格式 recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); // 設置封裝格式 recorder.setFormat("flv"); // 一秒內(nèi)的幀數(shù) recorder.setFrameRate(frameRate); // 兩個關鍵幀之間的幀數(shù) recorder.setGopSize(frameRate); // 設置音頻通道數(shù),與視頻源的通道數(shù)相等 recorder.setAudioChannels(grabber.getAudioChannels()); startTime = System.currentTimeMillis(); System.out.println("開始初始化幀抓取器"); // 初始化幀錄制器,例如數(shù)據(jù)結(jié)構(gòu)(音頻流、視頻流指針,編碼器), // 調(diào)用av_guess_format方法,確定視頻輸出時的封裝方式, // 媒體上下文對象的內(nèi)存分配, // 編碼器的各項參數(shù)設置 recorder.start(); System.out.println("幀錄制初始化完成,耗時[{}]毫秒"); System.out.println(System.currentTimeMillis()-startTime); Frame frame; startTime = System.currentTimeMillis(); System.out.println("開始推流"); long videoTS = 0; int videoFrameNum = 0; int audioFrameNum = 0; int dataFrameNum = 0; // 假設一秒鐘15幀,那么兩幀間隔就是(1000/15)毫秒 int interVal = 1000/frameRate; // 發(fā)送完一幀后sleep的時間,不能完全等于(1000/frameRate),不然會卡頓, // 要更小一些,這里取八分之一 interVal/=8; // 持續(xù)從視頻源取幀 while (null!=(frame=grabber.grab())) { videoTS = 1000 * (System.currentTimeMillis() - startTime); // 時間戳 recorder.setTimestamp(videoTS); // 有圖像,就把視頻幀加一 if (null!=frame.image) { videoFrameNum++; } // 有聲音,就把音頻幀加一 if (null!=frame.samples) { audioFrameNum++; } // 有數(shù)據(jù),就把數(shù)據(jù)幀加一 if (null!=frame.data) { dataFrameNum++; } // 取出的每一幀,都推送到SRS recorder.record(frame); // 停頓一下再推送 Thread.sleep(interVal); } System.out.println("推送完成,視頻幀[{}],音頻幀[{}],數(shù)據(jù)幀[{}],耗時[{}]秒"); System.out.println(videoFrameNum); System.out.println(audioFrameNum); System.out.println(dataFrameNum); System.out.println((System.currentTimeMillis()-startTime)/1000); // 關閉幀錄制器 recorder.close(); // 關閉幀抓取器 grabber.close(); } public static void main(String[] args) throws Exception { grabAndPush(MP4_FILE_PATH, SRS_PUSH_ADDRESS); }}
// 創(chuàng)建管道輸出流 pipedOutputStream = new PipedOutputStream(); // 創(chuàng)建管道輸入流 pipedInputStream = new PipedInputStream(); try { // 將管道輸入流與輸出流連接 此過程也可通過重載的構(gòu)造函數(shù)來實現(xiàn) pipedOutputStream.connect(pipedInputStream); } catch (IOException e) { e.printStackTrace(); }
2.第二步將我們?nèi)〉玫牧鲗懙焦艿乐校ū热绱笕A的sdk、海康的、或者其他從硬件設備中獲取的,如果是本機自帶的攝像頭可以直接用JavaCV提供的錄制功能,而不需要使用管道進行推送)關鍵詞:實現(xiàn),例子,版本,設計,視頻,直播
微信公眾號
版權(quán)所有? 億企邦 1997-2025 保留一切法律許可權(quán)利。