ExoPlayer利用自定义DataSource实现直接播放AES加密音频

原创 2017年11月14日 19:00:07

开局一张图 应该都见过

dataSource.png

ExoPlayer源码浅析

ExoPlayer官方文档

ExoPlayer GitHub

需求与适用范围

首先本文的适用范围是使用ExoPlayer框架时,直接解密播放已经经过AES加密过(或者类似需求)的音频或者视频,是利用官方demo内DefaultDataSourceFactory与DefaultDataSource改造而来。有需求就可以继续往下看了

0.故事的开始

故事的开始还得从新需求开始说起。公司新开了一个旅游项目,其中主要功能就是播放在线或者本地音频,这个非常非常平常的需求,让我开始EXO之旅。

为什么不使用最平常简单的MediaPlayer,因为程序员喜新厌旧哇 嘻嘻。

其中我们最主要的需求就是,下载下来的本地音频需要加密存放在本地,在播放的时候进行解密播放,以保证数据的安全性,在这里选用了常见的AES加解密。

相信这些需求应该的比较常见的类型了,在选用Exoplayer之后,在线播放什么的肯定不用花费过多时间了,但在进行本地播放的时候碰到了问题。

当初在查询ExoPlayer文档的时候是知道和看中了它的自定义资源播放功能的,官方文档和各种源码解析都说它有强大的自定义功能,但是等到我真正要实现我功能的时候,我感觉ExoPlayer是一个孤儿…

为什么在GitHub上8000+星星的ExoPlayer在百度上的资料这么少(想抄都没地方抄)。。 百度Google了一圈下来,折腾完ExtractorsFactory,折腾Mp3Extractor,完全摸错了门。后来在stackOverflow 看到终于一篇关于自定义dataSource的提问,至此找到问题解决入口。

思路与使用方式

关于ExoPlayer的各种源码、使用解析在这就不赘叙了。正常的情况下,使用ExoPlayer默认的DefaultDataSource是完成不了直接播放AES加密音频的。

最开始,要实现播放,只能先把AES音频解密成正常的MP3音频,这样,就完全打破了最初我们要把本地下载下来的音频加密存放与播放的预想,解密过后的音频总会在文件夹中显示出来,并以正常的MP3存在,这样音频就毫无安全性可言了,也就是说没有完成需求。

为解决能直接播放AES加密音频的问题,我们采用的是自定义改写DataSourceFactory类,改写DataSource类,复用官方demo中Aes128DataSource类来完成的。

只需要改写这一系列DataSource类文件,就可以完成我们直接播放AES音频文件的需求,下面逐个进行解析。

通过前期查看和研究ExoPlayer源码我们观察到,这一系列的资源提供、分拆与解析过程在ExtractorMediaSource下的ExtractorMediaPeriod类有很清晰的体现。

DefaultExtractorInput.png

而我们需要实现的需求中,本质上只是把MP3文件加密了一次而已,它在播放的时候,最终只需要在拆解提供资源的时候把加密的文件流解密成正常的MP3流,给ExtractorMediaSource提供正常的MP3流即可,所以并不需要再过多的进行其他复杂操作就可以完成此需求。

那么我实现的就是重写DefaultDataSourceFactory资源提供工厂类,改造DefaultDataSource,根据文件类型判断,加密的音频使用Aes128DataSource类拆解进行解密,未加密或者在线的URL继续使用getFileDataSource或者getContentDataSource原本默认的实现进行拆解。最后只需要用我们改写的factory替换默认的DataSourceFactory就可以完成我们的需求了。

TestPlayerActivity.png

1.AitripDataSourceFactory

AitripDataSourceFactory是重写的DataSourceFactory工厂类,直接copyDefaultDataSource而来。主要是重写了createDataSource()方法,用于进入资源选择类AitripDataSource

    @Override
    public AitripDataSource createDataSource() {
        return new AitripDataSource(context, listener, baseDataSourceFactory.createDataSource());
    }

2.AitripDataSource

改写DataSource内open方法,用于扩展数据源选择方案,以让其正确选择到我们的解密Aes128DataSource类,注释基本可以解释一切。因为我在本地加密的文件是在文件名后又添加了.aitrip字段,所以只需根据文件名判断是否包含.aitrip,选择正确的DataSource解析即可。

    @Override
    public long open(DataSpec dataSpec) throws IOException {
        Assertions.checkState(dataSource == null);
        // Choose the correct source for the scheme.
        //选择正确的数据源方案
        String scheme = dataSpec.uri.getScheme();
        //如果URI是一个本地文件路径或本地文件的引用。
        Timber.e("解密:000000," + scheme + ",path:" + dataSpec.uri.getPath());
        if (Util.isLocalFileUri(dataSpec.uri)) {
            //如果路径尾包含aitrip的文件名,使用解密类
            if (dataSpec.uri.getPath().endsWith(".aitrip")) {
                Aes128DataSource aes128DataSource =
                        new Aes128DataSource(getFileDataSource(), Aikey.getBytes(), Aikey.getBytes());
                dataSource = aes128DataSource;
            } else {//否则,正常解析mp3
                if (dataSpec.uri.getPath().startsWith("/android_asset/")) {
                    dataSource = getAssetDataSource();
                } else {
                    dataSource = getFileDataSource();
                }
            }
        } else if (SCHEME_ASSET.equals(scheme)) {
            dataSource = getAssetDataSource();
        } else if (SCHEME_CONTENT.equals(scheme)) {
            dataSource = getContentDataSource();
        } else if (SCHEME_RTMP.equals(scheme)) {
            dataSource = getRtmpDataSource();
        } else {
            dataSource = baseDataSource;
        }
        // Open the source and return.
        return dataSource.open(dataSpec);
    }

3.Aes128DataSource

直接copy自官方demo的AesDataSource,其本用于HLS文件播放列表解密的,但是很巧的是采用了解密方式,对我也同样适用,所以并不需要改动任何一行代码。。
它主要是实现open后,利用Aes解密方式解密文件流提供给上层使用。

  @Override
  public long open(DataSpec dataSpec) throws IOException {
    Cipher cipher;
    try {
      cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
    } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
      throw new RuntimeException(e);
    }

    Key cipherKey = new SecretKeySpec(encryptionKey, "AES");
    AlgorithmParameterSpec cipherIV = new IvParameterSpec(encryptionIv);

    try {
      cipher.init(Cipher.DECRYPT_MODE, cipherKey, cipherIV);
    } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
      throw new RuntimeException(e);
    }

    cipherInputStream = new CipherInputStream(
        new DataSourceInputStream(upstream, dataSpec), cipher);

    return C.LENGTH_UNSET;
  }

就这样。。 没了。 完成之后再看回来,这么简单。。。 全是copy。。 说个毛啊。。。。 然而,对于刚接触ExoPlayer,想要实现类似功能,而网上没啥参考的小伙伴来说,应该是有点用的,特此记录。。。。

结束

有帮助就点星星哟

下载源码慢慢看喔

有什么疑问或者建议欢迎issues留言噢

ExoPlayerTest小demo的Github地址

版权声明:本文为博主原创文章,若有疑问请联系博主。

相关文章推荐

android exoplayer 自定义界面,播放器

  • 2017年03月27日 19:46
  • 30.57MB
  • 下载

使用spring aop+自定义注解实现动态使用DataSource

Spring AOP的两种实现方式:JDK动态代理和CGLIB动态代理 1、JDK动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。 2、CGL...
  • thjnemo
  • thjnemo
  • 2015年04月01日 10:47
  • 2163

使用ExoPlayer播放res/raw中音频资源

前言关于ExoPlayer的使用,很多前人已经帮我们翻译了官方文档,例如ExoPlayer使用,这里不再阐述。 但是上述文章一般就是将官方文档翻译一下,针对具体的使用,可能会遇到很多难以解决问题。正...

JS实现HTML5音频播放自定义UI

此处首先放一个MDN上关于H5音频播放的文档 使用 HTML5 音频和视频 音频audio标签使用之后是默认带进度条的,所以audio标签中的属性是我们可以定制,选择将其全部隐藏就是了,然后自己实...

Exoplayer+Exomedia打造自定义播放器(二)

Exomedia源码分析 VideoControls 播放器基类 继承自RelativeLayout,播放器基类。作用是提供一些默认的播放器基本组件;提供反射控制器布局、显示加载进度、播放器隐藏显...

Android视频播放器Exoplayer自定义

一.背景 最近要用播放器播放视频,以前没怎么做过,但是听说过很多款第三方播放器,Android系统是没有播放器,以前写过一个用surfaceview写的简易播放器,但是离商用还有很远的路,所以经过一番...

html5自定义音频播放界面

  • 2015年01月19日 18:22
  • 11.7MB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:ExoPlayer利用自定义DataSource实现直接播放AES加密音频
举报原因:
原因补充:

(最多只允许输入30个字)