闽公网安备 35020302035485号
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));
}
}
}