使用 ExoPlayer 再现加密视频 [英] Reproducing encrypted video using ExoPlayer

查看:49
本文介绍了使用 ExoPlayer 再现加密视频的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在 Android 中使用 ExoPlayer,并且我'我试图重现本地存储的加密视频.

I'm using ExoPlayer, in Android, and I'm trying to reproduce an encrypted video stored locally.

ExoPlayer 的模块化允许创建可以注入 ExoPlayer 的自定义组件,这似乎是事实.事实上,经过一些研究,我意识到为了完成这个任务,我可以创建一个自定义的数据源并覆盖 open()read()close().

The modularity of ExoPlayer allows to create custom components that can be injected in the ExoPlayer, and this seems the case. Indeed, after some researches I realized that for achive that task I could create a custom DataSource and overriding open(), read() and close().

我也找到了这个解决方案,但实际上在这里整个文件在一个步骤中被解密并存储在一个清晰的输入流中.这在许多情况下都很好.但是如果我需要复制大文件怎么办?

I have also found this solution, but actually here the entire file is decrypted in one step and stored in a clear inputstream. This can be good in many situation. But what if I need to reproduce big file?

所以问题是:如何在 ExoPlayer 中重现加密视频,即时"解密内容(而不解密整个文件)?这可能吗?

So the question is: how can I reproduce encrypted video in ExoPlayer, decrypting content "on-fly" (without decrypting the entire file)? Is this possibile?

我尝试创建一个具有 open() 方法的自定义数据源:

I tried creating a custom DataSource that has the open() method:

@Override
    public long open(DataSpec dataSpec) throws FileDataSourceException {
        try {
            File file = new File(dataSpec.uri.getPath());

            clearInputStream = new CipherInputStream(new FileInputStream(file), mCipher);

            long skipped = clearInputStream.skip(dataSpec.position);
            if (skipped < dataSpec.position) {
                throw new EOFException();
            }
            if (dataSpec.length != C.LENGTH_UNBOUNDED) {
                bytesRemaining = dataSpec.length;
            } else {
                bytesRemaining = clearInputStream.available();
                if (bytesRemaining == 0) {
                    bytesRemaining = C.LENGTH_UNBOUNDED;
                }
            }
        } catch (EOFException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        opened = true;
        if (listener != null) {
            listener.onTransferStart();
        }

        return bytesRemaining;
    }

这是 read() 方法:

And this is the read() method:

@Override
public int read(byte[] buffer, int offset, int readLength) throws FileDataSourceException {
        if (bytesRemaining == 0) {
            return -1;
        } else {
            int bytesRead = 0;

                int bytesToRead = bytesRemaining == C.LENGTH_UNBOUNDED ? readLength
                        : (int) Math.min(bytesRemaining, readLength);
            try {
                bytesRead = clearInputStream.read(buffer, offset, bytesToRead);
            } catch (IOException e) {
                e.printStackTrace();
            }

            if (bytesRead > 0) {
                if (bytesRemaining != C.LENGTH_UNBOUNDED) {
                    bytesRemaining -= bytesRead;
                }
                if (listener != null) {
                    listener.onBytesTransferred(bytesRead);
                }
            }

            return bytesRead;
        }
    }

如果我传递的是一个明文文件而不是编码文件,并且只是删除 CipherInputStream 部分,那么它工作正常,而不是加密文件我得到这个错误:

If instead of an encoded file I pass a clear file, and just remove the CipherInputStream part, then it works fine, instead with encrypted file I obtain this error:

    Unexpected exception loading stream
java.lang.IllegalStateException: Top bit not zero: -1195853062
at com.google.android.exoplayer.util.ParsableByteArray.readUnsignedIntToInt(ParsableByteArray.java:240)
at com.google.android.exoplayer.extractor.mp4.Mp4Extractor.readSample(Mp4Extractor.java:331)
at com.google.android.exoplayer.extractor.mp4.Mp4Extractor.read(Mp4Extractor.java:122)
at com.google.android.exoplayer.extractor.ExtractorSampleSource$ExtractingLoadable.load(ExtractorSampleSource.java:745)
at com.google.android.exoplayer.upstream.Loader$LoadTask.run(Loader.java:209)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:423)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818)

编辑:

加密视频是这样生成的:

the encrypted video is generated in this way:

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec keySpec = new SecretKeySpec("0123456789012345".getBytes(), "AES");
IvParameterSpec ivSpec = new IvParameterSpec("0123459876543210".getBytes());
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);

outputStream = new CipherOutputStream(output_stream, cipher);

然后将 outputStream 保存到一个 File 中.

Then the outputStream is saved into a File.

推荐答案

最终我找到了解决方案.

Eventually I found the solution.

我对加密算法使用了无填充,这样:

I used a no-padding for the encryption algorithm, in this way:

cipher = Cipher.getInstance("AES/CTR/NoPadding", "BC");

这样加密文件的大小和明文文件的大小保持不变.所以现在我创建了流:

so that the size of the encrypted file and the clear file size remain the same. So now I created the stream:

cipherInputStream = new CipherInputStream(inputStream, cipher) {
    @Override
    public int available() throws IOException {
         return in.available();
    }
};

这是因为 Java 文档中提到了 ChiperInputStream.available()

This is because the Java documentation says about ChiperInputStream.available() that

这个方法应该被覆盖

实际上我认为它更像是 MUST,因为从该方法中检索到的值通常非常奇怪.

and actually I think is it more like a MUST, because the values retrieved from that method are often really strange.

就是这样!现在它完美运行.

And that is it! Now it works perfectly.

这篇关于使用 ExoPlayer 再现加密视频的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆