iOS上播放视频,http协议中应用rang请求头。视频格式MP4是正确的,但是你的后台没有对ios的视频播放器做适配。如果想要在iOS上播放视频,那么必须在http协议中应用rang请求头。对于有的朋友还对ios播放器http的range标记不是很懂。我再讲解下。视频文件总长度是123456789。
<!-webkit-playsinline="true"/*这个属性是ios 10中设置可以让视频在小窗内播放,即不全屏播放*/ playsinline="true"/*I0s微信浏览器支持小窗内播放*/ x-webkit-airplay="allow"/*使此视频支持ios的AirPlay功能*/ x5-video-player-type="h5”/*启用H5播放器,是wechat?安卓版特性*/ x5-video-player-fullscreen="true”/*全屏设置,设置为true是防止横屏*/> --> <video autoplay class="video" v-if="urlType === 'video'" :src="previewUrl" controls type="video/mp4" webkit-playsinline="true" playsinline="true" x5-playsinline="true" x-webkit-airplay="allow" x5-video-player-fullscreen="true" x5-video-player-type="h5" ></video>后端
/** * 分段下载 * 堆代码 duidaima.com * @param bucket * @param fileName * @param response */ @GetMapping("file/range/{bucket}/{fileName}") public void fileRangeIgnoreToken(@PathVariable String bucket, @PathVariable String fileName, HttpServletRequest request, HttpServletResponse response) { String property = System.getProperty("user.dir"); String filePath = property + "/" + fileName; log.info("新生文件的路径:{}", filePath); File file = new File(filePath); InputStream inputStream = minioTemplate.getObject(bucket, fileName); try { FileUtils.copyInputStreamToFile(inputStream, file); this.rangeVideo(request, response, file, fileName); } catch (Exception e) { e.printStackTrace(); log.error("分段发送文件出错,失败原因:{}", Throwables.getStackTraceAsString(e)); } finally { FileUtil.del(file); } } /** * 新增视频加载方法,解决ios系统vedio标签无法播放视频问题 * * @param request * @param response * @param file * @param fileName * @throws FileNotFoundException * @throws IOException */ public void rangeVideo(HttpServletRequest request, HttpServletResponse response, File file, String fileName) throws FileNotFoundException, IOException { RandomAccessFile randomFile = new RandomAccessFile(file, "r");//只读模式 long contentLength = randomFile.length(); log.info("获取导的contentLength={}", contentLength); String range = request.getHeader("Range"); int start = 0, end = 0; if (range != null && range.startsWith("bytes=")) { String[] values = range.split("=")[1].split("-"); start = Integer.parseInt(values[0]); if (values.length > 1) { end = Integer.parseInt(values[1]); } } int requestSize = 0; if (end != 0 && end > start) { requestSize = end - start + 1; } else { requestSize = Integer.MAX_VALUE; } response.setContentType("video/mp4"); response.setHeader("Accept-Ranges", "bytes"); response.setHeader("ETag", fileName); response.setHeader("Last-Modified", new Date().toString()); //第一次请求只返回content length来让客户端请求多次实际数据 if (range == null) { response.setHeader("Content-length", contentLength + ""); } else { //以后的多次以断点续传的方式来返回视频数据 response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);//206 long requestStart = 0, requestEnd = 0; String[] ranges = range.split("="); if (ranges.length > 1) { String[] rangeDatas = ranges[1].split("-"); requestStart = Integer.parseInt(rangeDatas[0]); if (rangeDatas.length > 1) { requestEnd = Integer.parseInt(rangeDatas[1]); } } long length = 0; if (requestEnd > 0) { length = requestEnd - requestStart + 1; response.setHeader("Content-length", "" + length); response.setHeader("Content-Range", "bytes " + requestStart + "-" + requestEnd + "/" + contentLength); } else { length = contentLength - requestStart; response.setHeader("Content-length", "" + length); response.setHeader("Content-Range", "bytes " + requestStart + "-" + (contentLength - 1) + "/" + contentLength); } } ServletOutputStream out = response.getOutputStream(); int needSize = requestSize; randomFile.seek(start); while (needSize > 0) { byte[] buffer = new byte[4096]; int len = randomFile.read(buffer); if (needSize < buffer.length) { out.write(buffer, 0, needSize); } else { out.write(buffer, 0, len); if (len < buffer.length) { break; } } needSize -= buffer.length; } randomFile.close(); out.close(); }上面这段代码可以解决本文开头提到的两个问题。
// 堆代码 duidaima.com @GetMapping("file/{bucket}/{fileName}") @IgnoreUserToken @IgnoreClientToken public void fileIgnoreToken(@PathVariable String bucket, @PathVariable String fileName, HttpServletResponse response) { if (fileName.toLowerCase().contains(".mov")) { log.info("进入mov文件转码"); File source = null; File target = null; try { InputStream inputStream = minioTemplate.getObject(bucket, fileName); source = new File("/" + IdUtil.simpleUUID() + ".mov"); target = new File("/" + IdUtil.simpleUUID() + ".mp4"); FileUtils.copyInputStreamToFile(inputStream, source); AudioAttributes audio = new AudioAttributes(); audio.setCodec("libmp3lame"); audio.setBitRate(new Integer(800000));//设置比特率 audio.setChannels(new Integer(1));//设置音频通道数 audio.setSamplingRate(new Integer(44100));//设置采样率 VideoAttributes video = new VideoAttributes(); // video.setCodec("mpeg4"); video.setCodec("libx264"); video.setBitRate(new Integer(3200000)); video.setFrameRate(new Integer(15)); EncodingAttributes attrs = new EncodingAttributes(); attrs.setOutputFormat("mp4"); attrs.setAudioAttributes(audio); attrs.setVideoAttributes(video); Encoder encoder = new Encoder(); encoder.encode(new MultimediaObject(source), target, attrs); IoUtil.copy(new FileInputStream(target), response.getOutputStream()); } catch (Exception e) { log.error("转码文件出错,失败原因:{}", Throwables.getStackTraceAsString(e)); } finally { log.info("删除临时文件"); FileUtil.del(source); FileUtil.del(target); } } else { try { IoUtil.copy(minioTemplate.getObject(bucket, fileName), response.getOutputStream()); } catch (IOException e) { log.error("读取文件出错,失败原因:{}", Throwables.getStackTraceAsString(e)); } } }