如何解决浏览器播放视频自动停止

请回答的朋友认真读一遍我的问题,不要无脑的复制粘贴。万分感谢
使用bootstrap框架开发一个培训系统,可以播放视频文件(MP4),服务端使用WinServer + apache-tomcat-9.0.17
前端使用了DPlayer、VideoJS、H5的video标签都试过了,总是播几分钟后就自动停止了,再点播放按钮就无任何反应了。

DPlayer的错误提示:视频加载失败,前端debug抓到的错误信息:PIPELINE_ERROR_DECODE: Failed to send audio packet for decoding: {timestamp=142869333 duration=21333 size=428 is_key_frame=1 encrypted=0}。
VideoJS的错误提示是:ERROR: (CODE:3 MEDIA_ERR_DECODE) The media playback was aborted due to a corruption problem or because the media used features your browser did not support.

代码以DPlayer插件为例:
function initVideo(containerId, path, zlbh, spzlid, rywcqkid) {
    var duration = 0.0;//学习时长
    var currentTime = Number(new Date());//当前观看时间
    var sfytjwczt = false;//是否已提交完成状态
    var dplayer = new DPlayer({
        container: document.getElementById(containerId),
        autoplay: false,
        theme: 'red',
        loop: false,
        lang: 'zh-cn',
        screenshot: false,
        hotkey: true,
        preload: 'auto',
        logo: '',
        volume: 0.7,
        mutex: true, //阻止多个播放器同时播放
        video: {
            url: path, //视频路径
            pic: ctx+"img/xszlmrfm.png", //视频封面
            thumbnails: ctx+"img/xszlmrfm.png", //视频缩略图
            type: 'auto',
        },
    });

    dplayer.seek('0.01'); //跳转到指定时间位置

    dplayer.on('playing', function () {
        //console.log("播放 playing");
        //间隔两秒则自动隐藏
        setTimeout(function(){
            //为播放器添加隐藏工具条样式
            document.getElementById("dplayer").classList.add("dplayer-hide-controller");
        }, 2000);
    });
    dplayer.on('timeupdate', function () {
        //console.log("timeupdate ", currentTime, currentTimeTmp, duration, dplayer.video.currentTime, dplayer.video.duration);
        if(duration > dplayer.video.duration * 80 / 100){//观看总时长的80%,才算为已学习
            if(!sfytjwczt){
                gxxxjl();
                gxwcqk();//更新完成情况
                sfytjwczt = true;
            }
        }else{
            var currentTimeTmp = Number(new Date());
            duration += (currentTimeTmp - currentTime) * 1.0 / 1000;
            currentTime = currentTimeTmp;
        }
    });
    // 进行监听
    dplayer.on('play', function () {
        currentTime = Number(new Date());
        //console.log("play", currentTime, dplayer.video.currentTime);
        tjxxjl();//点击播放按钮了,添加一次学习记录
    });
    dplayer.on('error', function () {
        debugger
        console.log("播放结束", dplayer.video.currentTime);
    });
    dplayer.on('progress', function () {
        localStorage.setItem('video.history.' + spzlid, dplayer.video.currentTime);
    });
    dplayer.on('seeking', function () {
        currentTime = Number(new Date());
        //console.log("seeking", currentTime, dplayer.video.currentTime);
    });

    var prefixXxjl = ctx + "syxt/xxjl";//学习记录的Api
    //添加学习记录
    function tjxxjl() {
        $.ajax({
            type: "post",
            url: prefixXxjl + "/tjxxjl",
            data: {
                "zlbh": zlbh
            },
            success: function(data) {
                if (data.code == web_status.SUCCESS) {
                }else{
                    $.modal.alertWarning(data.msg);
                }
            }
        });
    }

    //更新学习记录
    function gxxxjl() {
        $.ajax({
            type: "post",
            url: prefixXxjl + "/gxxxjl",
            data: {
                "zlbh": zlbh,
                "wcxx": 1,//1是完成学习
            },
            success: function(data) {
                if (data.code == web_status.SUCCESS) {
                    dplayer.notice("恭喜你,已完成此文件资料的学习", 3000);
                }else{
                    $.modal.alertWarning(data.msg);
                }
            }
        });
    }

    //更新完成情况
    function gxwcqk() {
        if(rywcqkid == undefined || rywcqkid == null || rywcqkid == ""){
            return;
        }
        $.ajax({
            type: "post",
            url: ctx + "syxt/wdrw/gxwcqk",
            data: {
                "rywcqkid": rywcqkid,
                "ywc": 1,//1是完成任务
            },
            success: function(data) {
                if (data.code == web_status.SUCCESS) {
                }else{
                    $.modal.alertWarning(data.msg);
                }
            }
        });
    }
}


<div class="form-group" id="spzlDiv">
        <label class="col-sm-3 control-label">视频资料:</label>
        <div class="col-sm-8" style="padding-bottom: 15px;">
            <div id="dplayer"></div>
        </div>
    </div>

<script th:src="@{/js/DPlayer/DPlayer.min.js}"></script>
<script th:src="@{/js/DPlayer/MyDPlayer.js}"></script>
<script th:inline="javascript">

                var path = ctx + spzlFileInfo.filePath;
                path = path.replace("//", "/");
                initVideo('dplayer', path, zlglXskczl.zlbh, zlglXskczl.spzlid, rywcqkid);

</script>
只说DPlayer吧,正常播放几分钟后,视频左下角报错:视频加载失败,然后视频就停住了,点任何按钮都无反应,debugger截图如下:

img

在Chrome浏览器或Chrome内核的浏览器上都会有此问题,但是在火狐浏览器就没事,完成正常。试了几个前端视频插件都不行。使用工具处理了mp4文件,转换成H264编码(原本应该也是此编码的),依然有问题。怀疑是和tomcat的配置有关系吗?但是我没有搜到合适的解决方案
请问懂浏览器播放视频的朋友们,如何才能在Chrome及Chrome内核的浏览器中正常播放视频?

可能是你的音频源有问题,你可以换一个音频再试试

tomcat 配置文件传输大小了吗,

还是视频格式问题

我发现你的播放器的一些事件(如timeupdate)在播放时会被不断触发,而你的代码中会在这个事件中不断执行一些逻辑,比如更新学习时长,更新完成情况,甚至还有设置定时器隐藏工具条。

这些频繁执行的逻辑可能会导致浏览器的性能问题,从而导致视频停止播放。我建议你把这些逻辑的执行频率降低,比如每5秒执行一次,或者只在视频播放结束时执行一次。

除了这些,你可以尝试把播放器的预加载设置为"none",或者把视频的类型设置为"mp4",看看是否能够解决问题。

另外,你可以在浏览器的控制台中查看是否有错误信息

看你的报错信息,比较像视频解码报错了,解码失败,所以就无法继续播放了,建议本地找一个确认没有问题的视频测试一下

终于解决了这个问题,原因是直接给前端的视频地址是文件的全路径,对于文件较大的视频就会出现此问题。
解决办法就是写了一个解析接口,在后台代码中控制文件分段大小。视频地址示例:http://ip:port/****/videoPlayer/video?fileId=6034

服务端接口代码如下:

package com.ruoyi.project.videoPlayer.controller;

import com.google.common.net.HttpHeaders;
import com.ruoyi.framework.config.RuoYiConfig;
import com.ruoyi.framework.web.controller.BaseController;
import com.ruoyi.project.system.file.domain.FileInfo;
import com.ruoyi.project.system.file.service.IFileInfoService;
import org.apache.shiro.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;

/**
 * 视频文件管理Controller
 *
 * @author 
 * @date 2022-12-28
 */
@Controller
@RequestMapping("/videoPlayer")
public class VideoPlayerController extends BaseController {

    @Autowired
    private IFileInfoService fileInfoService;

    private static final Logger log = LoggerFactory.getLogger(VideoPlayerController.class);

    @GetMapping("/video")
    public void video(HttpServletRequest request, HttpServletResponse response, String fileId) {
        RandomAccessFile targetFile = null;
        OutputStream outputStream = null;
        try {
            FileInfo fileInfo = fileInfoService.selectFileInfoById(Long.valueOf(fileId));
            String filePath = fileInfo.getFilePath();
            String fileName = fileInfo.getFileName();

            outputStream = response.getOutputStream();
            response.reset();
            //获取请求头中Range的值
            String rangeString = request.getHeader("Range");

            String path = RuoYiConfig.getUploadPath() + filePath.replace("/profile/upload", "");
            //打开文件
            File file = new File(path);
            if (file.exists()) {
                //使用RandomAccessFile读取文件
                targetFile = new RandomAccessFile(file, "r");
                long fileLength = targetFile.length();
                long requestSize = fileLength;
                //分段下载视频
                if (StringUtils.hasText(rangeString)) {
                    //从Range中提取需要获取数据的开始和结束位置
                    long requestStart = 0, requestEnd = 0;
                    String[] ranges = rangeString.split("=");
                    if (ranges.length > 1) {
                        String[] rangeDatas = ranges[1].split("-");
                        requestStart = Integer.parseInt(rangeDatas[0]);
                        if (rangeDatas.length > 1) {
                            requestEnd = Integer.parseInt(rangeDatas[1]);
                        }
                    }
                    if (requestEnd != 0 && requestEnd > requestStart) {
                        requestSize = requestEnd - requestStart + 1;
                    }
                    //根据协议设置请求头
                    response.setHeader(HttpHeaders.ACCEPT_RANGES, "bytes");
                    response.setHeader(HttpHeaders.CONTENT_TYPE, "video/mp4");
                    if (!StringUtils.hasText(rangeString)) {
                        response.setHeader(HttpHeaders.CONTENT_LENGTH, fileLength + "");
                    } else {
                        long length;
                        if (requestEnd > 0) {
                            length = requestEnd - requestStart + 1;
                            response.setHeader(HttpHeaders.CONTENT_LENGTH, "" + length);
                            response.setHeader(HttpHeaders.CONTENT_RANGE,
                                    "bytes " + requestStart + "-" + requestEnd + "/" + fileLength);
                        } else {
                            length = fileLength - requestStart;
                            response.setHeader(HttpHeaders.CONTENT_LENGTH, "" + length);
                            response.setHeader(HttpHeaders.CONTENT_RANGE,
                                    "bytes " + requestStart + "-" + (fileLength - 1) + "/" + fileLength);
                        }
                    }
                    //文段下载视频返回206
                    response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
                    //设置targetFile,从自定义位置开始读取数据
                    targetFile.seek(requestStart);
                } else {
                    //如果Range为空则下载整个视频
                    response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + fileName + "");
                    //设置文件长度
                    response.setHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(fileLength));
                }

                //从磁盘读取数据流返回
//                byte[] cache = new byte[1024];//1k缓存
                byte[] cache = new byte[1024 * 300];//
                log.info("开始读取视频,视频路径==" + path + ";requestSize==" + requestSize);
                while (requestSize > 0) {
                    int len = targetFile.read(cache);
                    if (requestSize < cache.length) {
                        outputStream.write(cache, 0, (int) requestSize);
                    } else {
                        outputStream.write(cache, 0, len);
                        if (len < cache.length) {
                            break;
                        }
                    }
                    requestSize -= cache.length;
                    log.info("requestSize-=cache.length==" + requestSize + ";视频路径==" + path);
                }
                log.info("结束视频读取");
            }
            outputStream.flush();
        } catch (
                Exception e) {
        } finally {
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                }
            }
            if (targetFile != null) {
                try {
                    targetFile.close();
                } catch (IOException e) {
                }
            }
        }
    }
}


使用此接口后,完美解决问题

浏览器播放视频自动停止的问题可能有很多原因。下面是一些可能导致这种情况的原因,以及可能的解决方案:

视频文件本身可能损坏,导致浏览器无法正常播放。在这种情况下,你可以尝试使用其他播放器播放视频,以确定视频文件是否有问题。如果确实有问题,你可以尝试使用一些工具来修复视频文件。

可能是浏览器的缓存问题。你可以尝试清除浏览器的缓存,然后再次尝试播放视频。

还可能是视频文件的格式问题。不同的浏览器对视频文件格式的支持情况可能不同,如果视频文件的格式不被浏览器支持,就可能导致视频播放失败。你可以尝试使用视频转换工具将视频文件转换为浏览器支持的格式,然后再次尝试播放。

视频文件的存储位置也可能是问题的原因。如果视频文件的存储位置不可访问,就会导致视频播放失败。你可以尝试确保视频文件的存储位置是可访问的,并检查视频文件的路径是否正确。

还有可能是网络问题。如果你在使用视频文件时遇到了网络问题,那么就可能导致视频播放失败。你可以尝试检查网络连接是否正常,并检查视频文件是否可以在线播放。

还有可能是浏览器本身的问题。如果你的浏览器是一个老版本的浏览器,可能会出现兼容性问题,导致视频播放失败。你可以尝试更新浏览器或者使用其他浏览器进行尝试。

希望这些建议能帮助你解决浏览器播放视频自动停止的问题。