使用ExoPlayer再现加密视频 [英] Reproducing encrypted video using ExoPlayer
问题描述
我在Android中使用 ExoPlayer ,而我'试图重现本地存储的加密视频。
ExoPlayer的模块化允许创建可以注入到ExoPlayer中的自定义组件,这种情况似乎如此。事实上,经过一些研究,我意识到,为了完成任务,我可以创建一个自定义的DataSource并覆盖 open()
, read()
和 close()
。
我尝试创建一个具有open()方法的自定义DataSource:
@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()方法:
@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部分,那么它工作正常,而是加密文件我得到这个错误:
意外的异常加载流
java.lang.IllegalStateException:顶部位不为零:-1195853062
com.google.android.exoplayer.util.ParsableByteArray.readUnsignedIntToInt(ParsableByteArray.java:240)
在com.google $。$。 com.google.android.exoplayer.extractor.ExtractorSampleSource $ ExtractingLoadable.load(ExtractorSampleSource.java:745)
在com.google.android.exoplayer.upstream.Loader $ LoadTask.run(Loader.java:209)
在java.util.concurrent.Executors $ RunnableAdapter.call(Executors.java:423)
在java.util.concurrent.FutureTask.run(FutureTask .java:237)
在java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
在java.util.concurrent.ThreadPoolExecutor $ Worker.run(ThreadPoolExecutor.java:588)$
$ p $ <$ $编辑:
加密视频以这种方式生成:
密码密码= 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保存到文件中。
解决方案最终我找到了解决方案。
我以这种方式使用无填充的加密算法: / p>
cipher = Cipher.getInstance(AES / CTR / NoPadding,BC);
使加密文件的大小和清除文件大小保持不变。所以现在我创建了流:
cipherInputStream = new CipherInputStream(inputStream,cipher){
@Override
public int available()throws IOException {
return in.available();
}
};
这是因为Java文档关于 ChiperInputStream.available() code>
,实际上我认为它更像是必须的,因为从该方法检索的值通常很奇怪。
那就是!现在它的效果很好。
I'm using ExoPlayer, in Android, and I'm trying to reproduce an encrypted video stored locally.
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?
So the question is: how can I reproduce encrypted video in ExoPlayer, decrypting content "on-fly" (without decrypting the entire file)? Is this possibile?
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;
}
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;
}
}
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)
EDIT:
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);
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();
}
};
This is because the Java documentation says about ChiperInputStream.available()
that
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屋!