【java 使用 ffmpeg 将rtsp转hls】

提示

java使用 SpringBoot 框架
jdk使用 1.8版本

海康

通过访问 OpenAPI 文档中心的方式可以验证 API 网关是否安装成功,访问地址
http://IP:Port/artemis-portal/document 打开文档中心(其中 IP:Port 以及 http 头与登录门户页面信息保
持一致)。海康运管中心

相关文件

海康调试工具 下载
海康在线 文档 (需登录)

开发准备

环境
ip,prot门户登录地址,注意端口要和所选的协议匹配(http 默认端口 80,https 默认 端口 443,如有修改,以实际端口为准);
参数
appkey ,secret Appkey/Secret:合作方 Key 和 Secret(从运管中心-状态监控-API 网关-API 管理-合作方 管理,点开具体合作方获得);
参数来源

工具使用

  • 视频WEB插件
    浏览器插件播放
    根据cameraIndexCode播放

  • OpenApi 签名生成工具
    根据签名使用postman调用调试

   X-Ca-Key:appKey,即 AK。
   X-Ca-Signature:签名,以 appSecret(即 SK)为密钥,
   X-Ca-Signature-Headers:请求头,填写有值字段
   以上字段为必填值

获取签名
获取签名 使用postman调取接口
postman调用接口

  • OpenAPI 接口测试工具
    根据参数直接调取
    直接调取

引入海康依赖
pom.xml

<dependency>
    <groupId>com.hikvision.ga</groupId>
    <artifactId>artemis-http-client</artifactId>
    <version>1.1.3</version>
</dependency>

java文件

package com.landy.framework.web.controller;
// 引入类
import com.alibaba.fastjson.JSONObject;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import com.hikvision.artemis.sdk.ArtemisHttpUtil;
import com.hikvision.artemis.sdk.config.ArtemisConfig;
import com.landy.framework.common.Response; //自定义 Spring Boot 相应包
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;

获取监控列表

@RestController
@RequestMapping("/api/camera")
@Api(tags = "1-海康威视接口")
@ApiSupport(order = 1)
public class TWaternetCameraController {

    @ApiOperation(value = "1-查询视频列表", notes = "调用海康威视接口查询视频列表")
    @GetMapping("/cameraSearch")
    public Response cameraSearch(Integer pageNo, Integer pageSize) {
        return Response.ok(this.GetCameraPreviewURL("/api/resource/v2/encodeDevice/search", null, pageNo, pageSize));
    }

    @ApiOperation(value = "2-查询摄像头视频流", notes = "调用海康威视接口查询摄像头视频流")
    @GetMapping("/cameraPreviewUrl")
    public Response cameraPreviewUrl(String cameraCode) {
        return Response.ok(this.GetCameraPreviewURL("/api/video/v1/cameras/previewURLs", cameraCode, null, null));
    }

    public String GetCameraPreviewURL(String url, String indexCodes, Integer pageNo, Integer pageSize) {
    	/*
    		url 根据官方文档或提供接口切换
    	*/
    	
        /**
         * STEP1:设置平台参数,根据实际情况,设置host appkey appsecret 三个参数.
         * {appkey:"******",secret:"******",ip:"***",port:***
         */
        ArtemisConfig.host = "ip:port"; // artemis网关服务器ip端口
        ArtemisConfig.appKey = "******"; // 秘钥appkey
        ArtemisConfig.appSecret = "******";// 秘钥appSecret

		/**
		* 两种都可
		*
		  ArtemisConfig artemisConfig = new ArtemisConfig();
          artemisConfig.setHost("ip:port"); //平台(nginx)IP和端口
          artemisConfig.setAppKey("******"); //合作方key
          artemisConfig.setAppSecret("******");//合作方Secret
		*/
		
        /**
         * STEP2:设置OpenAPI接口的上下文
         */
        final String ARTEMIS_PATH = "/artemis";

        /**
         * STEP3:设置接口的URI地址
         */
        final String previewURLsApi = ARTEMIS_PATH + url;

        Map<String, String> path = new HashMap<String, String>(2) {
            {
                put("https://", previewURLsApi);//根据现场环境部署确认是http还是https
            }
        };

        /**
         * STEP4:设置参数提交方式
         */
        String contentType = "application/json";

        /**
         * STEP5:组装请求参数
         */
        JSONObject jsonBody = new JSONObject();

         // jsonBody.put("userId", "***");
        // jsonBody.put("cameraIndexCode", "***");
        // jsonBody.put("streamType", 0);
        // jsonBody.put("protocol", "rtsp");
        // jsonBody.put("transmode", 1);
        // jsonBody.put("expand", "streamform=ps");

        if (StringUtils.isNotBlank(indexCodes)) {
            // jsonBody.put("cameraIndexCode", new String[]{indexCodes});
            jsonBody.put("cameraIndexCode", indexCodes);
            jsonBody.put("protocol", "rtsp");
        } else {
            if (pageNo == null) {
                pageNo = 1;
            }
            if (pageSize == null) {
                pageSize = 1000;
            }
            jsonBody.put("pageNo", pageNo);
            jsonBody.put("pageSize", pageSize);
        }
        String body = jsonBody.toJSONString();

        /**
         * STEP6:调用接口
         */
        String result = ArtemisHttpUtil.doPostStringArtemis(path, body, null, null,
                contentType, null);// post请求application/json类型参数
        return result;
    }
}

ffmpeg转换视频流

视频流可获取 hls 格式不需要转换

java文件

package com.landy.framework.web.controller;
// 引入类
import com.alibaba.fastjson.JSONObject;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import com.hikvision.artemis.sdk.ArtemisHttpUtil;
import com.hikvision.artemis.sdk.config.ArtemisConfig;
import com.landy.framework.common.Response; //自定义 Spring Boot 相应包
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;

视频流转换

使用ffmpeg转换视频流 可自定义转换格式

@RestController
@RequestMapping("/api/camera")
@Api(tags = "2-视频流转换")
@ApiSupport(order = 1)
public class videoStreamController {

    private FileUtil fileUtil;
    @Value("${mapFile.path}")  // 存储地址
    private String filePath;
    @Value("${mapFile.address}") // 外层文件夹名
    private String address;
    @Value("${mapFile.ip}")  // 映射ip
    private String ip;

    @ApiOperation(value = "1-RTSP转HLS视频流", notes = "调用FFmpeg转视频流")
    @GetMapping("/changePreviewUrl")
    public Response changePreviewUrl(String rtspUrl, String codeName) {
        this.stopFFmpeg();
        return Response.ok(this.GetCameraPreviewURL(rtspUrl, codeName));
    }

    @ApiOperation(value = "2-停止视频流转换", notes = "停止FFmpeg转视频流")
    @GetMapping("/stopPreviewUrl")
    public Response stopPreviewUrl() {
        return Response.ok(this.stopFFmpeg());
    }

    private Process ffmpegProcess;
    private String outputName = "";

    public String GetCameraPreviewURL(String url, String codeName) {
        // 工作目录
        String currentPath = System.getProperty("user.dir");
        // 获取当前类的路径
        // String classPath = GetClassPathExample.class.getResource("/").getPath();
        // 获取类路径中的资源文件路径
        // JAR 包中,它将返回 null
        // String resourcePath = GetResourcePathExample.class.getClassLoader().getResource("example.txt").getPath();

         this.getFilePath();

        // RTSP 输入 URL 和 HLS 输出设置
        String rtspUrl = "rtsp://your_rtsp_stream_url";
        rtspUrl = url;
        /*
        	url: "rtsp://用户名:密码@ip:port/Streaming/Channels/101"; // 密码不能有@
        	url: "rtsp:ip:port/openUrl/U7p2Rgl // 获取摄像头对应的rtsp地址
        */
        // 
        if (StringUtils.isNotBlank(codeName)) {
            int nameIdx =  codeName.lastIndexOf("\\");
            outputName = codeName;
            if (nameIdx != -1) {
                outputName = codeName.substring(0, nameIdx);
            }
        }
        String outputPath = filePath.replaceAll("/", "\\\\") + outputName + ".m3u8";
        //outputPath = "D:\\apache-tomcat-8\\webapps\\output\\" + outputName + ".m3u8";

        // FFmpeg 命令
        String[] command = {
                "ffmpeg",
                "-i", rtspUrl,
                "-c:v", "libx264",
                "-preset", "fast",
                "-crf", "20",
                "-c:a", "aac",
                "-b:a", "128k",
                "-f", "hls",
                "-hls_time", "10",
                "-hls_list_size", "10",
                "-hls_flags", "delete_segments",
                outputPath
        };

        String result = "";
        try {
            // 创建 ProcessBuilder 实例
            ProcessBuilder processBuilder = new ProcessBuilder(command);
            processBuilder.redirectErrorStream(true);

            // 启动进程
            ffmpegProcess = processBuilder.start();

            /*
            // 读取并打印 FFmpeg 的输出
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;

            //reader = new BufferedReader(new InputStreamReader(process.getErrorStream()));

            System.out.println(reader.readLine());

            while ((line = reader.readLine()) != null) {
                if (result == "") {
                    result = "/output/"+outputName+".m3u8";
                    System.out.println(line);
                }
            }
             */

            result = "/file/"+outputName+".m3u8";

            //new Thread(() -> {
                String line = "";
                File dir = new File(outputPath);
                int j = 0;
                try (BufferedReader reader = new BufferedReader(new InputStreamReader(ffmpegProcess.getInputStream()))) {
					// 生成文件后返回视频路径
                    while (line != null || dir.exists()) {
                        line = reader.readLine();
                        dir = new File(outputPath);
                        System.out.println("line " + Integer.toString(j) + line);
                        System.out.println("dire " + Integer.toString(j) + " " + dir.exists());
                        if (line == null || dir.exists()) break;
                        j++;
                    }

                    if (line != null) return  result;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            //}).start();

            // 等待进程结束
            int exitCode = ffmpegProcess.waitFor();
            result = Integer.toString(exitCode);
            System.out.println("FFmpeg exited with code " + exitCode);
            ffmpegProcess = null;
            outputName = "";
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
        return result;
    }

    public String stopFFmpeg() {
    	// 单进程
        String result = "false";
        if (ffmpegProcess != null) {
            ffmpegProcess.destroy(); // 终止FFmpeg进程
            System.out.println("FFmpeg process stopped.");
            ffmpegProcess = null;
            result = "true";
        }
        if (outputName != "") {
            this.delFileInPath();
            outputName = "";
        }
        return result;
    }


    /**
     * 判断创建文件
     * */
    public void getFilePath() {
        // 创建目录
        File dir = new File(filePath);
        boolean start = dir.exists();
        if (!dir.exists()) {
            dir.mkdirs();
            start = dir.exists();
        }
        System.out.println(start);
    }
    /**
     * 删除文件夹下的文件
     * */
    public void delFileInPath() {
        File directory = new File(filePath);

        // 检查目录是否存在并且是一个目录
        if (directory.exists() && directory.isDirectory()) {
            // 获取目录中的所有文件和子目录
            File[] files = directory.listFiles();

            if (files != null) {
                for (File file : files) {
                    if (file.isFile()) {
                        if (outputName != "") {
                            if (file.getName().indexOf(outputName) != -1) {
                                file.delete();
                            }
                        } else {
                            System.out.println("文件: " + file.getName());
                        }
                    } else if (file.isDirectory()) {
                        System.out.println("目录: " + file.getName());
                    }
                }
            } else {
                System.out.println("无法列出目录中的文件");
            }
        } else {
            System.out.println("指定的路径不是一个有效的目录");
        }
    }
}

导入

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小白∪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值