admin管理员组

文章数量:1533920

概述

​ 本章主要讲解实际工作中遇到的问题,以及如何解决并实现,下面是音视频开发的常用工具,下载地址就不贴了。

VLC media player 视频流播放工具

MediaInfo 视频信息查看工具

开发人员:app(负责开发板),java(负责服务器,进行编解码)

一.通过海康sdk获取视频流

​ 海康提供的sdk录像功能,然后保存到sd卡中,当客户端有人想看这个视频时,去sd卡中拿视频

​ 实现步骤:

1.当开发板生成视频,将视频路径以及设备id信息通知后端服务器,服务器将数据展示在web界面中.
2.当web界面,有人想看某个视频,将视频路径传到服务器,服务器通过websocket和开发板通信(这里之所以用websocket,是因为板子没有公网ip,所以它可以访问服务器,因为nat或其他技术,但是服务器无法直接访问这块板子,所以需要,板子一上电,就和服务器建立一个双通道的websocket,这样双方就可以交换数据了),根据协议,封装数据报,并发送。
3,开发板根据和服务器协商好的协议(类似于网络协议,便于双方知道对方想干什么),知道服务器要看视频,然后解析视频路径,然后去sd卡中获取,然后将文件流(stream)传到服务器,服务器接收到流,在返回给web客户端。

遇到的问题:

1.浏览器如何播放视频?
2.sd卡中保存的视频的封装格式是MPEG-PS,普通播放器无法播放,需要转码

问题1:浏览器如何播放视频?

1.前端可以拿到文件流,具体就是byte数组,然后转成base64,赋值给vedio标签,但是这样有个问题如果视频较小,这么做可以,但是如果视频是几十兆的这种,时间会比较久,低配的电脑,可能浏览器会出现卡死的情况,所以这种方案即便可以,但是用户体验也不是太好,因为base64转码会比原文件,多出三分之一的大小,这种实现的最大弊端就是,由于是通过文件流传输,从开发板到服务器,再从服务器到web,耗时太久,网络不是很好的话,耗时还会增加,所以这种方式肯定是有问题的。

**2.开发板将文件流传给服务器,服务器将流上传至文件服务器,然后返回一个公网可以访问的url,然后将这个视频和url做一个映射(map),别人在看这个视频,就可以通过映射关系,拿到url,将url返回到web,vedio可以直接播放,这种方式有个好处,多个人看一个视频的时候,视频只需要传递一次,就可以,至于为什么开发板生成视频后就上传到文件服务器,因为一些原因(板子有sd卡,视频生成是因为发生了故障xxx等,所以不需要放到服务器),但是需要定时器,在文件服务器去删除视频,目前我们使用是这种方式。

问题2: sd卡中保存的视频的封装格式是MPEG-PS,普通播放器无法播放,需要转码

解决问题1的时候发现一个问题,将开发板上的文件流,上传到服务器,发现服务器的视频无法播放,经MediaInfo 查看,发现,视频的封装格式为MPEG-PS,这种视频,普通的播放器无法播放的,更别说浏览器自带的了,可以通过万能播放器软件等播放,但是,我们最终是要把视频展示在h5当中,所以,需要进行转码,转成浏览器可以播放的视频格式,查看一些博客和资料,最后发现,可以通过ffmpeg 这个软件,进行编解码,具体资料以及使用方式,可以去雷神的博客学习https://blog.csdn/leixiaohua1020/article/details/15811977,注意不同的版本所支持的编解码器不同,根据业务需要使用的编解码器,去下载指定的版本,linux部署的时候踩了不少坑,这里给的下载地址都是可以放心使用的,作者都亲测过.

这里具体说明使用方式:
windows安装:
进入官网,选择需要下载的文件:
官方网址

下载以后,解压到所放置的文件夹:
	配置环境变量的步骤如下:
		1.打开我的电脑
		2.右键鼠标,点击属性
		3.选择高级系统设置,然后点击下方的环境变量
		4.在系统变量中,找到变量Path,点击值进行编辑,新增一条记录
		5.C:\ffmpeg\bin 你自己的安装目录,注意要到bin
		6.使用cmd命令,查看ffmpeg版本,如果正常显示

centos7安装教程:
(1).https://blog.csdn/u013314786/article/details/89682800
(2).https://linuxize/post/how-to-install-ffmpeg-on-centos-7/
linux中这两种方式不需要配置环境变量,如果想要在任意路径使用命令,就需要配置环境变量了,我这里使用的是方式(1)然后每次使用命令都需要到ffmpeg的安装目录下

mac安装
1.官网 https://evermeet.cx/ffmpeg/
mac可以通过brew直接安装的,但是作者这里一直失败,所以用了官网安装
2.选择进行下载
3.下载完成后进行解压,然后放到你自己的目录下,我这里直接放到桌面上的ffmpeg文件夹下了, 我本地安装目录就是:/Users/moch/Desktop/ffmpeg
4.配置环境变量,其实不配也可以,但是你只能在/Users/moch/Desktop/ffmpeg目录下使用ffmpeg命令,我这里配置了全局环境变量,具体如下
5.终端

1. cd ~
2. 使用ls -a可以看到.bash_profile文件
3. vi .bash_profile
//后面的值是你自己的安装路径,切记切记,如果你的path有多个,需要使用:分割
5. 添加 export PATH="/Users/moch/Desktop/ffmpeg"
//  让配置生效
6.  source .bash_profile

通过在命令行输入ffmpeg,如出现下图中显示的信息,表示安装成功,图中有版本号,以及当前版本中的编解码器,这里安装结束后,我们测试下,看看是否可以转码为我们想要的格式。

在windows命令行输入

ffmpeg -i c:/video1.MP4 -vcodec libx264 -c:a aac  c:/video.flv

其中 -i 后面是源数据 最后面的c:/video.flv是输出路径

至于libx264,aac一个是视频编码,一个是音频编码,因为我需要转成的视频格式为flv,flv的视频编码和音频编码,是avc和aac,至于参数的意思,这里就不详细讲解了,有兴趣的朋友可以去上面发的雷神的博客,或者网上自己学习

最终,生成的flv视频,通过MediaInfo查看

然后通过播放器,可以播放,说明视频转码成功,但是这里只是通过ffmpeg的软件进行操作的,实际操作中,不可能让用户这样操作,所以需要通过代码,调用ffmpeg的接口,进行转码,代码如下(前提是当前电脑的操作系统中,以及安装好了ffmpeg,并且可以正常使用)

配置类

package com.mochitec.hikbox.util;

import lombok.Data;

@Data
public class ConvertProperties {
    //linux下,路径肯定不是这样,是/root/xx这样子
    private  String inputPath = "C:\\Users\\mochu\\Desktop\\video.MP4"; 
    private  String outputPath =  "C:\\Users\\mochu\\Desktop\\video4.flv";
    private  String ffmpegPath = "C:\\wym\\ffmpeg\\bin\\"; //linux中,路径为 /root/moch/ffmpeg-4.4-amd64-static/ 你自己在linux服务器上安装的路径
    private  String convertFlag = "_wait";
}

package com.mochitec.hikbox.util;

import java.io.InputStream;

public class PrintStream extends Thread {
    InputStream __is = null;
    public PrintStream(InputStream is)
    {
        __is = is;
    }
    public void run()
    {
        try
        {
            while(this != null)
            {
                int _ch = __is.read();
                if(_ch != -1)
                    System.out.print((char)_ch);
                else break;
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}

转码类

package com.mochitec.hikbox.util;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class ConvertService {
    public boolean processMp4(String inputPath,String outputPath,String ffmpegPath) {
        List<String> command = new ArrayList<>();
        command.add(ffmpegPath + "ffmpeg");
        command.add("-r");
        command.add("25");
        command.add("-i");
        command.add(inputPath);
//        command.add("-c:v");
        command.add("-vcodec");
        command.add("libx264");
        command.add("-mbd");
        command.add("0");
        command.add("-c:a");
        command.add("aac");
        command.add("-s");
        command.add("720*720");
        command.add("-threads");  //指定同时启动线程执行数, 经测试到10再大速度几无变化
        command.add("25");
        command.add("-preset");
        command.add("ultrafast");
        command.add("-strict");
        command.add("-2");
        command.add("-pix_fmt");
        command.add("yuv420p");
        command.add("-movflags");
        command.add("faststart");
        command.add(outputPath);
        try {
            // 方案1
            //        Process videoProcess = Runtime.getRuntime().exec(ffmpegPath + "ffmpeg -i " + oldfilepath
            //                + " -ab 56 -ar 22050 -qscale 8 -r 15 -s 600x500 "
            //                + outputPath + "a.fl\\\\\\\\\\\\\v");
            // 方案2
            Process videoProcess = new ProcessBuilder(command).redirectErrorStream(true).start();
            new PrintStream(videoProcess.getErrorStream()).start();
            new PrintStream(videoProcess.getInputStream()).start();
            videoProcess.waitFor();
            System.out.println("转换完成");
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
     /**
     * 删除文件方法
     *
     * @param filepath
     */
    public void deleteFile(String filepath) {
        File file = new File(filepath);
        if (file.delete()) {
            System.out.println("文件" + filepath + "已删除");
        }
    }
}


package com.mochitec.hikbox;
import com.mochitec.hikbox.util.ConvertProperties;
import com.mochitec.hikbox.util.ConvertService;
class HikboxApplicationTests {
    public static void main(String[] args) {
        ConvertService convertService = new ConvertService();
        ConvertProperties convertProperties = new ConvertProperties();
        //参数为,源文件路径,目标文件路径,以及ffmpeg的安装路径
        convertService.processMp4(convertProperties.getInputPath(),
                convertProperties.getOutputPath(), convertProperties.getFfmpegPath());
    }

通过main进行调用,就可以转码了。至于实际业务中,调用,这些参数肯定是动态传递的,这里需要注意的是,在linux下,注意ffmpeg的安装路径,如果错误,程序运行后,会报找不到命令的错误,至此视频的转码工作完成,这里需要注意的是,需要源文件在服务器上存在,才可以这么做,所以我是先上传到服务器,然后将源文件删除掉的。最后将转换后的文件路径,传递给h5,linux下如果想让文件可以在公网访问,需要进行代理,一种是nginx,一种是tomcat,我这里使用的是tomcat,需要注意的是,如果想在url查看目录,需要配置tomcat的web.xml,默认是关闭查看的,因为这样有数据泄露。具体如何在tomcat配置如下

https://blog.csdn/wdays83892469/article/details/87944688

https://blog.csdn/wangduanqing5945/article/details/30266935
前端直接访问视频资源,会有跨域的问题,所以需要在tomcat里web.xml配置

<filter>  
    <filter-name>CORS</filter-name>  
    <filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>  
    <init-param>  
     <param-name>cors.allowOrigin</param-name>  
        <param-value>*</param-value>  
    </init-param>  
    <init-param>  
     <param-name>cors.supportedMethods</param-name>  
        <param-value>GET, POST, HEAD, PUT, DELETE</param-value>  
    </init-param>  
    <init-param>  
     <param-name>cors.supportedHeaders</param-name>  
        <param-value>Accept, Origin, X-Requested-With, Content-Type, Last-Modified</param-value>  
    </init-param>  
    <init-param>  
        <param-name>cors.exposedHeaders</param-name>  
        <param-value>Set-Cookie</param-value>  
    </init-param>  
    <init-param>  
        <param-name>cors.supportsCredentials</param-name>  
        <param-value>true</param-value>  
    </init-param>  
</filter>  
<filter-mapping>  
    <filter-name>CORS</filter-name>  
    <url-pattern>/*</url-pattern>  
</filter-mapping>

看到文章末尾的,可以看下的我的程序人生这篇文章,主要是讲我在编程这条路上的经历,祝愿对你有用,感谢!

本文标签: 音视频实战视频转码ffmpeg