视频进度条(雪碧图)

更新时间: 2025/01/23 13:46:55

雪碧图是在转码阶段生成的由多帧视频截图拼接成的一张大图,用于视频播放器进度条拖动时快速预览视频内容。本文介绍了网易云信播放器中雪碧图的概念、工作原理、以及详细的使用流程。

功能介绍

播放器上的雪碧图是一张由多帧截图拼接而成的图,用于在拖动进度条时通过缩略图快速预览视频内容。使用雪碧图具有以下好处:

  • 加载速度快:由于雪碧图只需要加载一张大图,而不是多张小图,可以减少网页的加载时间,提升用户体验。
  • 减少资源请求:通过将多帧截图合并成一张大图,使用更高效的压缩算法进行压缩,减少了请求服务器资源的次数,提高了加载速度和性能,更方便管理和维护。由于减少了资源请求次数,可以相应节省带宽成本。
  • 提升用户体验:用户可以通过快速预览视频内容,快速定位自己感兴趣的部分,提升了用户体验的同时还能提升页面的稳定性和视觉效果。

以下为一张雪碧图示例。该雪碧图中共包含 9 张从视频中截取的缩略小图。

image.png

基于上述雪碧图的进度条缩略图预览效果如下:

image.png

前提条件

根据本文操作前,请确保您已经完成了以下设置:

  • 按照您的需求,开通网易云信点播等产品。详细步骤请参考 开通或试用服务
  • 集成网易云信播放器 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)来进一步优化性能。

相关接口

如果您需要通过调用服务端接口实现快速预览视频内容的功能,请参考 创建预览图任务查询预览图结果

此文档是否对你有帮助?
有帮助
去反馈
  • 功能介绍
  • 前提条件
  • 实现流程
  • 第一步:初始化播放器
  • 方式一:通过 playerKit 组件
  • 方式二:通过播放器 SDK
  • 第二步:实现核心功能
  • 下载雪碧图
  • 计算缩略图索引
  • 获取缩略图
  • MD5 哈希
  • 逻辑优化
  • 相关接口