视频进度条(雪碧图)
更新时间: 2025/01/23 13:46:55
雪碧图是在转码阶段生成的由多帧视频截图拼接成的一张大图,用于视频播放器进度条拖动时快速预览视频内容。本文介绍了网易云信播放器中雪碧图的概念、工作原理、以及详细的使用流程。
功能介绍
播放器上的雪碧图是一张由多帧截图拼接而成的图,用于在拖动进度条时通过缩略图快速预览视频内容。使用雪碧图具有以下好处:
- 加载速度快:由于雪碧图只需要加载一张大图,而不是多张小图,可以减少网页的加载时间,提升用户体验。
- 减少资源请求:通过将多帧截图合并成一张大图,使用更高效的压缩算法进行压缩,减少了请求服务器资源的次数,提高了加载速度和性能,更方便管理和维护。由于减少了资源请求次数,可以相应节省带宽成本。
- 提升用户体验:用户可以通过快速预览视频内容,快速定位自己感兴趣的部分,提升了用户体验的同时还能提升页面的稳定性和视觉效果。
以下为一张雪碧图示例。该雪碧图中共包含 9 张从视频中截取的缩略小图。
基于上述雪碧图的进度条缩略图预览效果如下:
前提条件
根据本文操作前,请确保您已经完成了以下设置:
- 按照您的需求,开通网易云信点播等产品。详细步骤请参考 开通或试用服务。
- 集成网易云信播放器 SDK 到您的项目中。详细步骤请参考 集成播放器 SDK。
- 了解如何生成临时 Token,并在后续使用过程中生成 Token。详情请参考 临时 Token。
实现流程
从客户端请求播放权限到播放器 SDK 开始播放视频的整个过程如下图所示:
sequenceDiagram
autonumber
participant client as 应用客户端
participant server as 应用服务端
participant sdk as 网易云信播放器 SDK
participant service as 网易云信点播服务端
client->>server: 请求临时播放 Token
server->>server: 签发临时播放 Token
server-->>client: 下发临时播放 Token
client->>sdk: 初始化播放器
client->>sdk: 将临时播放 Token 传递给播放器 SDK<br>player.setVidDataSource(token, vid);
sdk->>service: 获取播放信息,包括雪碧图信息<br>(playerObserver.onFetchedVideoInfo 监听雪碧图信息)
service-->>sdk: 返回播放信息<br>(NEVideoInfo 对象包含雪碧图信息)
sdk-->>client: 缓存和下载雪碧图<br>downloadThumbs(Context context, NEVideoInfo imfo)
sdk->>sdk: 开始播放
临时 Token 的生成步骤请参考 接口概述 临时 Token 章节。
第一步:初始化播放器
集成播放器 SDK 后,您可以通过两种方式初始化播放器:
- 通过 playerKit 组件:NELivePlayer_Demo_Android_vx.x.x 压缩包中包含一个示例工程,提供了在播放器 SDK 基础上封装的 playerkit 组件。
- 通过播放器 SDK:LivePlayer_Android_SDK_vx.x.x:压缩包存放播放器 SDK 的 Java 依赖包和底层动态链接库。
方式一:通过 playerKit 组件
以下代码通过 playerKit 组件完成,使用了一个名为 VodPlayer 的视频播放器类,并通过 PlayerManager 进行初始化和配置,通过 VodPlayerObserver 监听视频播放器事件,可用于视频点播(VOD)场景。
Java// 注册
VodPlayer player = PlayerManager.buildVodPlayer(this, mVideoPath, options);
player.setVidDataSource(token, vid);
player.registerPlayerObserver(playerObserver, true);
// 监听
private VodPlayerObserver playerObserver = new VodPlayerObserver() {
@Override
public void onFetchedVideoInfo(NEVideoInfo videoInfo) {
Log.d(TAG, "videoInfo:" + videoInfo.toString());
}
}
方式二:通过播放器 SDK
以下代码展示了如何使用 NELivePlayer 类初始化视频播放器,并设置监听器以获取视频信息。
Java// 注册
NELivePlayer player = NELivePlayer.create(libLoader);
player.setVidDataSource(token, vid);
player.setOnFetchedVideoInfoListener(onFetchedVideoInfoListener);
// 监听
private NELivePlayer.OnFetchedVideoInfoListener onFetchedVideoInfoListener = new NELivePlayer.OnFetchedVideoInfoListener() {
@Override
public void onFetchedVideoInfo(NEVideoInfo videoInfo) {
Log.d(TAG, "videoInfo:" + videoInfo.toString());
}
};
第二步:实现核心功能
以下示例代码展示了雪碧图处理使用方式,适用于视频播放器中的缩略图预览功能。
下载雪碧图
- 检查是否需要下载雪碧图。
- 如果需要下载,则在后台线程中逐个下载雪碧图并保存到本地缓存目录,避免避免阻塞主线程以及重复下载。
Javapublic void downloadThumbs(Context context, NEVideoInfo imfo){
File dir = new File(context.getCacheDir().getAbsolutePath() + "/thumb/");
if (!dir.exists()) {
dir.mkdirs(); // 创建文件夹
}
//判断是否需要下载
boolean needDownload = false;
for(int i = 0; i < imfo.imageURLs.size(); i++) {
String imageUrl = imfo.imageURLs.get(i);
String path = dir.getAbsolutePath() + md5(imageUrl) + ".jpg";
File imageFile = new File(path);
if (!imageFile.exists()) {
needDownload = true;
}
}
//下载并保存雪碧图
if(needDownload) {
postRunnableIfNotExists(() -> {
for (int i = 0; i < imfo.imageURLs.size(); i++) {
String imageUrl = imfo.imageURLs.get(i);
String path = dir.getAbsolutePath() + md5(imageUrl) + ".jpg";
File imageFile = new File(path);
if (!imageFile.exists()) {
try {
URL url = new URL(imageUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
InputStream inputStream = new BufferedInputStream(connection.getInputStream());
byte[] data = new byte[1024];
int count;
FileOutputStream outputStream = new FileOutputStream(path);
while ((count = inputStream.read(data)) != -1) {
outputStream.write(data, 0, count);
}
outputStream.close();
inputStream.close();
connection.disconnect();
} catch (IOException e) {
Log.e(TAG, "Error downloading image: " + e.getMessage());
}
}
}
});
}
}
计算缩略图索引
根据播放位置(单位:毫秒)和雪碧图的时间间隔,计算当前缩略图的索引。
Javapublic int getCurrentThumbIndex(NEVideoInfo imfo, final long position){
if(imfo.interval == 0){
return 0;
}
return (int) (position / 1000 / imfo.interval);
}
获取缩略图
- 根据索引从雪碧图中提取缩略图。
- 使用
BitmapRegionDecoder从雪碧图中解码指定区域的缩略图,节省内存。
Javapublic Bitmap getThumbBitmap(Context context, NEVideoInfo imfo, int index) {
if (context == null || imfo.imageYLen == 0 || imfo.imageXLen == 0 || imfo.interval == 0) {
return null;
}
Rect rect = new Rect();
int spriteIndex = index / (imfo.imageYLen * imfo.imageXLen);
// 缩略小图在雪碧大图中的 index
index = index % (imfo.imageYLen * imfo.imageXLen);
rect.left = (index % imfo.imageXLen) * imfo.imageXSize;
rect.top = (index / imfo.imageXLen) * imfo.imageYSize;
rect.right = rect.left + imfo.imageXSize;
rect.bottom = rect.top + imfo.imageYSize;
try {
File dir = new File(context.getCacheDir().getAbsolutePath() + "/thumb/");
String path = dir.getAbsolutePath() + md5(imfo.imageURLs.get(spriteIndex)) + ".jpg" ;
BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(path, false);
Bitmap decodeBitmap = decoder.decodeRegion(rect, null);
decoder.recycle();
return decodeBitmap;
} catch (Exception e) {
Log.d(TAG, "getThumbBitmap Exception " + e);
}
return null;
}
MD5 哈希
生成输入字符串的 MD5 哈希值,用于生成缓存文件的唯一名称。
Javaprivate String md5(String input) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] messageDigest = md.digest(input.getBytes());
// Convert the byte array to a hexadecimal string
StringBuilder hexString = new StringBuilder();
for (byte b : messageDigest) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}
逻辑优化
在实际使用时,您可以按照以下建议继续优化实现逻辑:
- 错误处理:在下载和解码过程中,可以增加更多的错误处理逻辑,例如重试机制。
- 内存管理:在解码缩略图时,可以考虑使用内存缓存(如
LruCache)来进一步优化性能。
相关接口
此文档是否对你有帮助?





