前情提要:

1,部署好摄像设备的网关

2,可以正常请求海康摄像头视频数据:

需求描述: 需要下载海康历史视频数据, 设备检测到一些事件后会发送告警信息,收到告警后将这告警期间的这段视频保存下来;

**由于海康只提供七天的保存时间,所以要将这报警期间的视频保存到obs中;

工具类:

package com.wyj.government.utils;


import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.fileupload.disk.DiskFileItem;
import org.apache.http.HttpEntity;
import org.apache.http.ParseException;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartFile;

import java.io.*;


public class CommUtil {
    private static final Logger log = LoggerFactory.getLogger(CommUtil.class);

    public static CloseableHttpClient httpClient;
//    private static final int CONNECT_TIMEOUT = common.HttpClientConfig.getHttpConnectTimeout();// 设置连接建立的超时时间为10s
//    private static final int SOCKET_TIMEOUT = common.HttpClientConfig.getHttpSocketTimeout();
//    private static final int MAX_CONN = common.HttpClientConfig.getHttpMaxPoolSize(); // 最大连接数
//    private static final int Max_PRE_ROUTE = common.HttpClientConfig.getHttpMaxPoolSize();
//    private static final int MAX_ROUTE = common.HttpClientConfig.getHttpMaxPoolSize();

    /**
     * PUT操作命令
     *
     * @param url
     * @param Input
     * @return
     */
    public static String doPut(String host, short port, String UserName, String Password, String url, String Input) {
        String PutUrl = "http://" + host + ":" + port + url;

        HttpPut httpPut = new HttpPut(PutUrl);
        httpPut.setEntity(new StringEntity(Input, "UTF-8"));
        CredentialsProvider credsProvider = new BasicCredentialsProvider();
        credsProvider.setCredentials(new AuthScope(host, port),
                new UsernamePasswordCredentials(UserName, Password));
        httpClient = HttpClients.custom().setDefaultCredentialsProvider(credsProvider).build();
        CloseableHttpResponse responseBody = null;
        String response = "";
        try {
            // 由客户端执行(发送)Post请求
            responseBody = httpClient.execute(httpPut);
            // 从响应模型中获取响应实体
            HttpEntity responseEntity = responseBody.getEntity();
            System.out.println("响应状态为:" + responseBody.getStatusLine());
            if (responseEntity != null) {
                System.out.println("响应内容长度为:" + responseEntity.getContentLength());
                response = EntityUtils.toString(responseEntity);
                System.out.println("响应内容为:\n" + response);

            }
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (ParseException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                // 释放资源
                if (httpClient != null) {
                    httpClient.close();
                }
                if (responseBody != null) {
                    responseBody.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return response;


    }

    public static String doPost(String host, short port, String UserName, String Password, String url, String Input) {
        String PostUrl = "http://" + host + ":" + port + url;

        HttpPost httpPost = new HttpPost(PostUrl);
        httpPost.setEntity(new StringEntity(Input, "UTF-8"));
        CredentialsProvider credsProvider = new BasicCredentialsProvider();
        credsProvider.setCredentials(new AuthScope(host, port),
                new UsernamePasswordCredentials(UserName, Password));
        httpClient = HttpClients.custom().setDefaultCredentialsProvider(credsProvider).build();

        CloseableHttpResponse responseBody = null;
        String response = "";
        try {
            // 由客户端执行(发送)Post请求
            responseBody = httpClient.execute(httpPost);
            // 从响应模型中获取响应实体
            HttpEntity responseEntity = responseBody.getEntity();
            System.out.println("响应状态为:" + responseBody.getStatusLine());
            if (responseEntity != null) {
                System.out.println("响应内容长度为:" + responseEntity.getContentLength());
                response = EntityUtils.toString(responseEntity);
                System.out.println("响应内容为:\n" + response);

            }
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (ParseException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                // 释放资源
                if (httpClient != null) {
                    httpClient.close();
                }
                if (responseBody != null) {
                    responseBody.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return response;


    }

    //获得视频流
    public static byte[] doStreamPost(String host, short port, String UserName, String Password, String url, String Input) {
        String PostUrl = "http://" + host + ":" + port + url;

        HttpPost httpPost = new HttpPost(PostUrl);
        httpPost.setEntity(new StringEntity(Input, "UTF-8"));
        CredentialsProvider credsProvider = new BasicCredentialsProvider();
        credsProvider.setCredentials(new AuthScope(host, port),
                new UsernamePasswordCredentials(UserName, Password));
        httpClient = HttpClients.custom().setDefaultCredentialsProvider(credsProvider).build();
        byte[] byteArray = new byte[0];
        CloseableHttpResponse responseBody = null;
        try {
            // 由客户端执行(发送)Post请求
            responseBody = httpClient.execute(httpPost);
            // 从响应模型中获取响应实体
            HttpEntity responseEntity = responseBody.getEntity();
            if (responseEntity != null) {
                System.out.println("响应内容长度为:" + responseEntity.getContentLength());
                InputStream inputStream = responseEntity.getContent();
                ByteArrayOutputStream buffer = new ByteArrayOutputStream();
                int bytesRead;
                byte[] dataBuffer = new byte[1024];
                while ((bytesRead = inputStream.read(dataBuffer, 0, dataBuffer.length)) != -1) {
                    buffer.write(dataBuffer, 0, bytesRead);
                }
                buffer.flush();
                byteArray = buffer.toByteArray();
            }
            System.out.println("响应状态为:" + responseBody.getStatusLine());
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (ParseException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                // 释放资源
                if (httpClient != null) {
                    httpClient.close();
                }
                if (responseBody != null) {
                    responseBody.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return byteArray;


    }


    public static String doGet(String host, short port, String UserName, String Password, String url) {
        String GetUrl = "http://" + host + ":" + port + url;

        HttpGet httpget = new HttpGet(GetUrl);
        CredentialsProvider credsProvider = new BasicCredentialsProvider();
        credsProvider.setCredentials(new AuthScope(host, port),
                new UsernamePasswordCredentials(UserName, Password));
        httpClient = HttpClients.custom().setDefaultCredentialsProvider(credsProvider).build();
        CloseableHttpResponse responseBody = null;
        String response = "";
        try {
            // 由客户端执行(发送)Post请求
            responseBody = httpClient.execute(httpget);
            // 从响应模型中获取响应实体
            HttpEntity responseEntity = responseBody.getEntity();
            System.out.println("响应状态为:" + responseBody.getStatusLine());
            if (responseEntity != null) {
                System.out.println("响应内容长度为:" + responseEntity.getContentLength());
                response = EntityUtils.toString(responseEntity);
                System.out.println("响应内容为:\n" + response);

            }
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (ParseException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                // 释放资源
                if (httpClient != null) {
                    httpClient.close();
                }
                if (responseBody != null) {
                    responseBody.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return response;


    }

    public static String doPostUploadPhoto(String host, short port, String UserName, String Password, String url, String Input) throws UnsupportedEncodingException {
        String PostUrl = "http://" + host + ":" + port + url;

        HttpPost httpPost = new HttpPost(PostUrl);

        // 创建 MultipartEntityBuilder 实例
        MultipartEntityBuilder builder = MultipartEntityBuilder.create();
        builder.addTextBody("faceURL", Input, ContentType.APPLICATION_JSON);
        // 添加图片文件参数
        File file = new File("C:\\Users\\liujian54\\Desktop\\FDLib.jpg");
        builder.addBinaryBody("img", file, ContentType.create("image/jpeg"), file.getName());
        HttpEntity multipart = builder.build();
        // 设置请求体
        httpPost.setEntity(multipart);
        CredentialsProvider credsProvider = new BasicCredentialsProvider();
        credsProvider.setCredentials(new AuthScope(host, port),
                new UsernamePasswordCredentials(UserName, Password));
        httpClient = HttpClients.custom().setDefaultCredentialsProvider(credsProvider).build();

        CloseableHttpResponse responseBody = null;
        String response = "";
        try {
            // 由客户端执行(发送)Post请求
            responseBody = httpClient.execute(httpPost);
            // 从响应模型中获取响应实体
            HttpEntity responseEntity = responseBody.getEntity();
            System.out.println("响应状态为:" + responseBody.getStatusLine());
            if (responseEntity != null) {
                System.out.println("响应内容长度为:" + responseEntity.getContentLength());
                response = EntityUtils.toString(responseEntity);
                System.out.println("响应内容为:\n" + response);

            }
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (ParseException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                // 释放资源
                if (httpClient != null) {
                    httpClient.close();
                }
                if (responseBody != null) {
                    responseBody.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return response;
    }


    public static MultipartFile convert(byte[] bytes, String fileName) {
        // 创建 DiskFileItem 对象
        DiskFileItem diskFileItem = new DiskFileItem("file", "application/octet-stream", false, fileName, (int) bytes.length, null);
        try {
            // 将字节数组写入 DiskFileItem
            diskFileItem.getOutputStream().write(bytes);
            diskFileItem.getOutputStream().close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 使用 CommonsMultipartFile 将 DiskFileItem 转换为 MultipartFile
        return new CommonsMultipartFile(diskFileItem);
    }

    //类转换工具
    public static Boolean isJsonValid(String warnResult, Class<?> clazz) {
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            objectMapper.readValue(warnResult, clazz);
            return true;
        } catch (JsonProcessingException e) {
            return false;
        }
    }

}

下载历史视频

这里利用海康改的sdk下载

 //下载录像文件至本地后上传至obs
        byte[] doneStreamPost = CommUtil.doStreamPost(carmeraConfig.getHikHost(), carmeraConfig.getHikPort(), carmeraConfig.getHikUsername(), carmeraConfig.getHikPassword(), HikDataEnum.REQUEST_VIDEO_DOWNLOAD.getCode() + carmeraId, downLoadStr);
        String warnResult = new String(doneStreamPost);
        Boolean jsonValid = CommUtil.isJsonValid(warnResult, WarnResultError.class);

此时获得的doneStreamPost是ps封装的视频数据 (有可能会发生错误,此时返回的数据可能是错误信息,所以需要校验一下),这一点是一个很大的坑

然后进行视频转码,由于使用海康的转码的sdk需要签署一份协议,所以下面这个方法可以绕过签署协议(自己动手干吧),

下面这个方法没有使用真正的转码,而是通过视频录制的方式实现,性能一般;

    public static byte[] videoConvert(byte[] bytes, WhGovernmentCarmera whGovernmentCarmera) throws IOException, InterruptedException {
        // 写入临时文件
        File file = File.createTempFile(whGovernmentCarmera.getChannelName(), HikDataEnum.VIDEO_FORMAT_MP4.getCode());
        try (FileOutputStream outputStream = new FileOutputStream(file)) {
            outputStream.write(bytes);
        } catch (IOException e) {
            e.printStackTrace();
            throw new IOException("Failed to write bytes to temporary file", e);
        }

        String absolutePath = file.getAbsolutePath();
        String date2Time = DateUtils.date2Time(DateUtils.getNowDate());
        File mp4File = new File(date2Time + HikDataEnum.VIDEO_FORMAT_MP4.getCode());
        String mp4FileAbsolutePath = mp4File.getAbsolutePath();
        FFmpegLogCallback.set();
        Frame frame;
        byte[] byteArray;
        // 读取临时文件
        try (FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(absolutePath)) {
            grabber.setFrameRate(25);
            avutil.av_log_set_level(AV_LOG_ERROR);
            grabber.setVideoCodec(avcodec.AV_CODEC_ID_H264);
            grabber.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
            grabber.setSampleRate(44100);
            grabber.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
            grabber.setTimestamp(30 * 1000 * 1000);
            grabber.start();
            try (FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(mp4FileAbsolutePath, grabber.getImageWidth(), grabber.getImageHeight())) {
                recorder.setFormat("mp4");
                recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
                recorder.setVideoQuality(28);
                recorder.setVideoBitrate(2000000);
                recorder.setFrameRate(25);
                recorder.setSampleRate(44100);
                recorder.setAudioChannels(2);
                recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
                recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
                recorder.setVideoOption("preset", "slow");
                recorder.setVideoOption("tune", "zerolatency");
                recorder.setVideoOption("profile", "high");
                recorder.setVideoOption("level", "3.1");
                recorder.setOption("movflags", "faststart");
                recorder.start();

                // 从FrameGrabber中读取Frame并写入FrameRecorder中进行编码
                while ((frame = grabber.grab()) != null) {
                    recorder.record(frame);
                }
                try {
                    if (Objects.nonNull(recorder)) {
                        recorder.stop();
                        recorder.release();
                    }
                } catch (Exception exception) {
                    log.error("recorder告警视频处理异常,{}",exception.getCause());
                }
                try {
                    if (Objects.nonNull(grabber)) {
                        grabber.stop();
                        grabber.release();
                    }

                } catch (Exception exception) {
                    log.error("grabber告警视频处理异常,{}",exception.getCause());
                }
                log.info("告警视频处理完成");
                FileInputStream fileInputStream = new FileInputStream(mp4File);
                byteArray = new byte[(int) mp4File.length()];
                fileInputStream.read(byteArray);
                fileInputStream.close();

                // 清理临时文件
                if (!file.delete()) {
                    file.deleteOnExit();
                }
                //清理
                if (mp4File.exists()) {
                    mp4File.delete();
                }
            }
        }catch (Exception e){
            log.error("告警视频处理异常,{},将保存原视频信息",e.getCause());
            //如果发生异常则保存原视频信息
           byteArray=bytes;
        }
        return byteArray;
    }

将得到的视频文件上传obs

 // 将字节数组写入 .mp4 文件
            String fileName = projectName + ULIDUtils.getULid() + HikDataEnum.VIDEO_FORMAT_MP4.getCode();
            if (doneStreamPost.length > 0) {
                R<SysFile> upload = remoteFileService.upload(CommUtil.convert(videoConvert2, fileName));
                if (upload.getCode() == HttpStatus.SUCCESS) {
                    SysFile data = upload.getData();
                    url = data.getUrl();
                }
                log.info("设备:{},获取的告警视频url:{}", whGovernmentCarmera.getCarmeraId(), url);
            }

以上是海康视频转码的主要流程

在部署的时候还遇到了一个问题就是ffmpeg始终找不到linux的动态链接库,由于使用的是javacv这个包(里面封装好了linux版本的动态链接库),经排查后发现是因为jdk版本不对,

javacv官方说是要使用 oracle jdk 或者openjdk ,ibmjdk,于是切换了jdk版本 这回不报错了;

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐