Android 阿里云 视频直播安全下载踩坑实录

最近项目里面集成了阿里云直播的内容,其中需要相应的下载视频的功能,遇到了一些问题,在这里贴出来跟大家分享一下,

我在项目中使用的是基于阿里云高级播放器3.4.8的sdk,具体怎么导入sdk ,请移步官方文档,(ps:非常简单)

我下面所说的问题都是基于阿里云高级播放器 安全下载 所遇到的问题

1.加密文件的获取:

   加密文件可以使用官方demo里提供的算法工具类来生成sha1值,也可以使用 Terminal 来生成,

   使用Terminal 生成的方法是    https://www.cnblogs.com/arxive/p/6978364.html

生成sha1值以后,就可以在阿里云官网的管理后台根据sha1值来获取加密文件了,是一个.dat 文件,

2.加密文件放入手机中

  拿到加密文件以后,我们先把加密文件放入Android Studio 中的assets文件夹中(没有去创建,main包下面),然后,怎么放入手机中呢? 阿里云demo 里面有一个工具类,Commen ,直接拿过来用就可以了,稍后我会贴出来,

然后开始设置阿里云的初始化配置,只需要设置一次,视具体业务而定,

AliyunDownloadConfig config = new AliyunDownloadConfig();
//设置加密文件路径。使用安全下载的用户必须设置(在准备下载之前设置),普通下载可以不用设置。
Commen.getInstance(UIUtils.getContext()).copyAssetsToSD("encrypt", ConstantUtil.EncryptedPath);
config.setSecretImagePath(Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + ConstantUtil.EncryptedPath + "/encryptedApp.dat");
//设置保存路径。请确保有SD卡访问权限。

File file = new File(UIUtils.getSDCardCachePATH(ConstantUtil.VideoSavePath));
if (!file.exists()) {
    file.mkdir();
}
config.setDownloadDir(UIUtils.getSDCardCachePATH(ConstantUtil.VideoSavePath));
//设置最大下载个数,最多允许同时开启4个下载
config.setMaxNums(4);
AliyunDownloadManager.getInstance(UIUtils.getContext()).setDownloadConfig(config);

3.开始下载

阿里云提供三种方式下载,这里我采用了阿里云推荐的STS方式来下载,首先确认一个配置,后台生成STS的那个代码里面,不要设置policy,把request.setPolicy(policy);注释(否则下载会失败)

STS的下载需要凭证,一共三个字段  后台提供

String accessKeyId;
String accessKeySecret;
 String securityToken

在开始下载之前,要先注册阿里云下载的监听,便于在状态改变的时候及时更新本地数据库中的状态,

downloadManager.setDownloadInfoListener(mDownloadInfoListener);

然后,设置vids 过期的回调,目的是当vids过期之后能够重新请求vids的信息,防止下载失败,

在  new AliyunRefreshStsCallback() 的onSuccessData回调中要走本地请求下载凭证信息的接口,
downloadManager.setRefreshStsCallback(mRefreshStsCallback);

然后,准备下载,downloadManager.prepareDownloadMedia(aliyunVidSts);

在准备完成的回调函数里面加入下载队列,开始下载,

public void onPrepared(List list) {
//筛选你需要下载 的视频质量信息 开始下载
    for (int i = 0; i < list.size(); i++) {
        if (list.get(i).getQuality().equals(IAliyunVodPlayer.QualityValue.QUALITY_LOW)) {
            info = list.get(i);
        }
    }
downloadManager.addDownloadMedia(info);
downloadManager.startDownloadMedia(info);

到这里基本就可以下载了,遇到下载失败,可以查看阿里云文档或者提交工单进行咨询

4.下载视频的播放

下载视频播放,使用url 的播放方式进行播放,(跟直播传入的url 一样),传入本地文件的绝对路径作为url,

//播放方式二:使用URL播放(直播用户推荐使用)

String url = getIntent().getStringExtra("url");

AliyunLocalSource.AliyunLocalSourceBuilder asb = new AliyunLocalSource.AliyunLocalSourceBuilder();

asb.setSource(url);

//aliyunVodPlayer.setLocalSource(asb.build());

AliyunLocalSource mLocalSource = asb.build();

aliyunVodPlayer.prepareAsync(mLocalSource);

如果播放失败,请检查.dat 加密文件是否有效

到此,视频下载和阿里云服务器对接基本完成,

5.遇到问题以及常用解决方案

 在项目中有一个下载全部的功能,就是把所有下载项一下子都加入下载列表,这里需要控制视频的下载时机,必须在上一个视频

的onStart 方法或者onError方法回调之后,才控制开始下一个视频的下载,(add然后start),否则以前的会下载出错。我在这里使用的是List 集合,查找正在下载的info 的索引,然后开启 索引+1 的下载 的方式进行控制的,也可以使用其他方法

 

如果 错误码是 109 错误信息是 Invalid secret image ,那么去手机的文件管理检查你的应用目录下是否有生成的.dat 的加密文件

还有,查看视频下载进度的时候,最后一点会变得特别慢,这里阿里云也是给出了解释,因为:

1)当前版本的SDK3修改了3.4.6上的cpu占用过高的问题,SDK故意设置了结尾处速度下降的情况。2)在到90%后视频下载需要对视频文件本身进行合成处理,因此需要一段时间。

 

 

本地数据库的存储使用的是郭霖的Litepal 框架,挺好用的,给个github 链接:

https://github.com/LitePalFramework/LitePal/issues?page=1&q=is%3Aissue+is%3Aopen

(ps 更新于2019.1.17号:使用LitePal 的查询功能,where 条件查询支持占位符嵌套,比如说,我想查找符合某些条件的所有数据,就可以类似于这样写:LitePal.where("status in (?, ?)", "200", "300").find(DownLoadInfo.class)  )

 

下面贴一下 Commen 工具类源码 :

import android.content.Context;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * assets目录文件拷贝工具类
 */
public class Commen {
    private static Commen instance;
    private static final int SUCCESS = 1;
    private static final int FAILED = 0;
    private Context context;
    private FileOperateCallback callback;
    private volatile boolean isSuccess;
    private String errorStr;
    private ThreadPoolExecutor threadPoolExecutor;
    private String srcPath;
    private String sdPath;

    public static Commen getInstance(Context context) {
        if (instance == null) {
            synchronized (Commen.class) {
                if (instance == null) {
                    instance = new Commen(context);
                }
            }
        }
        return instance;
    }

    private Commen(Context context) {
        this.context = context;
    }

    private Handler handler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (callback != null) {
                if (msg.what == SUCCESS) {
                    callback.onSuccess();
                }
                if (msg.what == FAILED) {
                    callback.onFailed(msg.obj.toString());
                }
            }
            threadPoolExecutor.remove(runnable);
            context = null;
            instance = null;
        }
    };

    public Commen copyAssetsToSD(final String srcPath, final String sdPath) {
        this.srcPath = srcPath;
        this.sdPath = sdPath;

        threadPoolExecutor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.SECONDS, new LinkedBlockingDeque());
        threadPoolExecutor.execute(Executors.defaultThreadFactory().newThread(runnable));

        return this;
    }

    private Runnable runnable = new Runnable() {
        @Override
        public void run() {
            copyAssetsToDst(context, srcPath, sdPath);
            if (isSuccess) {
                handler.obtainMessage(SUCCESS).sendToTarget();
            } else {
                handler.obtainMessage(FAILED, errorStr).sendToTarget();
            }
        }
    };

    public void setFileOperateCallback(FileOperateCallback callback) {
        this.callback = callback;
    }

    private void copyAssetsToDst(Context context, String srcPath, String dstPath) {
        try {
            String[] fileNames = context.getAssets().list(srcPath);
            if (fileNames.length > 0) {
                File file = new File(Environment.getExternalStorageDirectory(), dstPath);
                if (!file.exists()) {
                    file.mkdirs();
                }
                for (String fileName : fileNames) {
                    // assets 文件夹下的目录
                    if (!"".equals(srcPath)) {
                        copyAssetsToDst(context, srcPath + File.separator + fileName,
                            dstPath + File.separator + fileName);
                    } else {
                        // assets 文件夹
                        copyAssetsToDst(context, fileName, dstPath + File.separator + fileName);
                    }
                }
            } else {
                File outFile = new File(Environment.getExternalStorageDirectory(), dstPath);
                InputStream is = context.getAssets().open(srcPath);
                FileOutputStream fos = new FileOutputStream(outFile);
                byte[] buffer = new byte[1024];
                int byteCount;
                while ((byteCount = is.read(buffer)) != -1) {
                    fos.write(buffer, 0, byteCount);
                }
                fos.flush();
                is.close();
                fos.close();
            }
            isSuccess = true;
        } catch (Exception e) {
            e.printStackTrace();
            errorStr = e.getMessage();
            isSuccess = false;
        }
    }

    public void onDestroy() {
        if (threadPoolExecutor != null && runnable != null) {
            threadPoolExecutor.remove(runnable);
        }

        if (handler != null) {
            handler.removeMessages(SUCCESS);
            handler.removeMessages(FAILED);
            handler = null;
        }
        instance = null;
        callback = null;
        context = null;

    }

    public interface FileOperateCallback {
        /**
         * copy success
         */
        void onSuccess();

        /**
         * copy fail
         * @param error 错误信息
         */
        void onFailed(String error);
    }

}

 

你可能感兴趣的:(Android 阿里云 视频直播安全下载踩坑实录)