Encryption 在ExoPlayer中脱机播放AES加密视频

Encryption 在ExoPlayer中脱机播放AES加密视频,encryption,aes,exoplayer,Encryption,Aes,Exoplayer,我正在尝试使用ExoPlayer从本地存储播放加密视频。 用于使用FFMPEG加密视频的命令如下: -i/storage/emulated/0/Download/20210125_193031.mp4-vcodec copy-acodec copy-c:v libx264-encryption\u scheme cenc aes ctr-encryption\u key b42ca3172ee4e69bf51848a59db9cd13-encryption\u kid 09e3672028f33

我正在尝试使用ExoPlayer从本地存储播放加密视频。 用于使用FFMPEG加密视频的命令如下:

-i/storage/emulated/0/Download/20210125_193031.mp4-vcodec copy-acodec copy-c:v libx264-encryption\u scheme cenc aes ctr-encryption\u key b42ca3172ee4e69bf51848a59db9cd13-encryption\u kid 09e3672028f3346ca5dd60ff6671e70/storage/emulated/0/Download/out\u enc.mp4

这是我的播放器的源代码:

public class PlayerActivity extends AppCompatActivity {
    private SimpleExoPlayer player;
    private DefaultDrmSessionManager drmSessionManager;

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_player);
        // Build the media item.
        PlayerView playerView = findViewById(R.id.video_view);
        player = new SimpleExoPlayer.Builder(this).build();
        playerView.setPlayer(player);
        //player.prepare();
        //FFMPEG command: -i /storage/emulated/0/Download/20210125_193031.mp4 -vf scale=-1:720 -c:v libx264 -encryption_scheme cenc-aes-ctr -encryption_key b42ca3172ee4e69bf51848a59db9cd13 -encryption_kid 09e367028f33436ca5dd60ffe6671e70 /storage/emulated/0/Download/out_enc.mp4
        //base 64 keys generated from: https://www.base64encode.org/
        //playVideo("/storage/emulated/0/Download/out_enc.mp4", "MDllMzY3MDI4ZjMzNDM2Y2E1ZGQ2MGZmZTY2NzFlNzA=", "YjQyY2EzMTcyZWU0ZTY5YmY1MTg0OGE1OWRiOWNkMTM=");
        playVideo("/storage/emulated/0/Download/out_enc.mp4", "CeNnAo8zQ2yl3WD/5mcecA", "tCyjFy7k5pv1GEilnbnNEw");
    }

    private void playVideo(String url, String keyID, String keyValue) {
        try {
            drmSessionManager = buildDrmSessionManager(Util.getDrmUuid(C.CLEARKEY_UUID.toString()), true, keyID, keyValue
            );
        } catch (Exception e) {
            e.printStackTrace();
        }
        player.setMediaSource(buildDashMediaSource(Uri.parse(url)));
        player.prepare();
        player.setPlayWhenReady(true);
    }

    private MediaSource buildDashMediaSource(Uri uri) {
        DefaultDataSourceFactory dashChunkSourceFactory = new DefaultDataSourceFactory(this, "agent");
        return new ProgressiveMediaSource.Factory(dashChunkSourceFactory)
                .setDrmSessionManager(drmSessionManager)
                .createMediaSource(uri);
    }

    private DefaultDrmSessionManager buildDrmSessionManager(UUID uuid, Boolean multiSession, String id, String value) {
/*        String base64Id = Base64.encodeToString(id.getBytes(), Base64.DEFAULT);
        String base64Value = Base64.encodeToString(value.getBytes(), Base64.DEFAULT);*/
        String keyString = "{\"keys\":[{\"kty\":\"oct\",\"k\":\""+value+"\",\"kid\":\""+id+"\"}],\"type\":\"temporary\"}";;
        LocalMediaDrmCallback drmCallback = new LocalMediaDrmCallback(keyString.getBytes());
        FrameworkMediaDrm mediaDrm = null;
        try {
            mediaDrm = FrameworkMediaDrm.newInstance(uuid);
        } catch (UnsupportedDrmException e) {
            e.printStackTrace();
        }
        return new DefaultDrmSessionManager(uuid, mediaDrm, drmCallback, null, multiSession);
    }

    @Override
    protected void onDestroy() {
        player.release();
        super.onDestroy();
    }
它是加密视频的链接。
主要问题:视频正在播放,但未解密。我遗漏了什么?

查看logcat输出,那里似乎没有任何DRM、AES或clearkey错误

然后查看视频文件本身,它似乎报告了一些问题:

但是,通过检查使用相同ffmpeg方法加密的其他示例文件,它们显示了类似的问题,因此这似乎是ffprobe以这种方式加密的文件的典型输出

然后用MP4解析器查看视频文件结构本身,以查看单个原子或头块,似乎没有PSSH框

PSSH框是包含ISOBMFF mp4文件加密相关数据的标题区域-这实际上是CENC规范中的可选字段,因此即使没有该字段,视频也有效

那么,一个显而易见的问题是,玩家如何知道视频是经过编码的?根据CENC规范,答案是:

  • 检测
  • 对于确定为ISO基本媒体文件格式[ISOBMFF]的流,可按如下方式检测此ISO通用加密(“cenc”)保护方案

    保护方案信令符合[ISOBMFF]。应用保护后,流类型将转换为视频的“encv”或音频的“enca”,并将保护方案信息框(“sinf”)添加到样本描述框(“stsd”)中的样本条目中。保护方案信息框(“sinf”)将包含一个方案类型框(“schm”),其中方案类型字段的值设置为“cenc”

    使用MP4分析仪(见下文)查看您的视频表明,它确实具有螺柱盒中显示为“encv”的流类型(以下检查工具的输出):

    使用相同的加密密钥测试ffplay本身的播放,表明视频确实成功播放:

    ffplay out_enc.mp4-解密_密钥b42ca3172ee4e69bf51848a59db9cd13

    但是,除非您提供解密密钥,否则普通玩家将无法播放它。在这种情况下,期望玩家标记错误是合理的,但在我检查的一些常见玩家(包括VLC)中似乎没有出现这种情况,因此Android上的ExoPlayer很可能也没有标记错误

    具体来看ExoPlayer,正如Duna在下面的评论中所指出的,并在这个GIT线程中概述的,ExoPlayer目前(2021年2月)没有阅读MP4的PSSH框,只阅读片段化的MP4。从这个线索:

    深入研究后,我发现ExoPlayer的Mp4Extractor实际上不读取pssh框。目前,我们仅在片段化MP4文件中读取此信息(使用FragmentedMP4提取器)。这意味着,即使在使用pssh框播放文件时,drmInitData仍然以null结尾,这意味着播放失败。当您最初提交问题时,我没有意识到这一限制,否则我会更早地标记它

    但是,查看Mp4Extractor代码时,它会检查“encv”,也会检查默认密钥id,这两个密钥id都存在于检查时生成的视频文件中。同样,如果ExoPlayer没有找到可以理解的格式的错误,或者找到了错误,但没有提供相应的密钥来播放文件,那么ExoPlayer标记错误也是合理的

    那么,如何对视频进行加密并可靠地播放呢

    您可以在android上使用ffplay,尽管我怀疑这不会太简单,基于过去在android上使用ffmpeg的经验

    还有一些更容易(出现)的例子值得一看,例如:

    你也可以看看DASH。如今,流式传输到移动设备的大多数媒体都使用DASH或HLS等流式协议——这些格式几乎总是将加密数据包含在“清单”或“索引”文件中,ExoPlayer肯定会识别这些数据。有在线教程和免费工具可以让你将视频打包到DASH中,包括添加加密。ExoPlayer团队提供有关下载和播放这些流的信息(撰写本文时链接正确):

    如果您想自己更详细地检查mp4文件,有各种免费工具,如:

    • (Duna在评论中强调,看起来非常好)

    你能分享任何相关的逻辑输出吗?这里是输出:对不起,我的评论被自动更正了-我想说的是“logcat”输出。您的问题看起来像是获取许可证的问题,这将有助于理解这一点。当然:您建议使用什么工具进行媒体加密?它需要在Android和iOS上工作——我的项目要求是在移动设备上进行加密itself@Duna,答案已更新。虽然它在理论上更为复杂,并且可能具有您不需要的功能,您可能会发现,使用DASH和HLS方法将允许您利用更多的主流工具,并且如果需要,还允许您在将来利用更强大的基于DRM的密钥保护。您能否共享所需的ffmpeg/mp4box命令行参数,以便将PSSH框包含在输出文件中?我可以和ffplayer一起玩,但是其他玩家使用clearkey时不行。@Duna-我想我最好问一个新问题,并包括你正在使用的命令