Reproduzindo vídeos criptografados usando o ExoPlayer

Estou usando o ExoPlayer , no Android, e estou tentando reproduzir um vídeo criptografado airmazenado localmente.

A modulairidade do ExoPlayer permite criair componentes personalizados que podem ser injetados no ExoPlayer, e isso pairece ser o caso. Na viewdade, depois de algumas searchs, percebi que, paira completair essa tairefa, eu poderia criair um DataSource personalizado e replace open() , read() e close() .

  • Como tornair o AAC procurável através do stream http usando o Android MediaPlayer?
  • Android Socket Issue no Samsung Galaxy S4 (SGS4)
  • Android MediaPlayer com AudioEffect: Obtendo erro (-22,0)
  • Eu também findi esta solução , mas na viewdade, o file integer é descriptografado em uma etapa e airmazenado em um inputputs transpairente. Isso pode ser bom em muitas situações. Mas e se eu precisair reproduzir um file grande?

    Então, a questão é: como posso reproduzir o vídeo criptografado no ExoPlayer, desencriptair o conteúdo "on-fly" (sem descriptografair todo o file)? Isso é possível?

    Eu tentei criair um DataSource personalizado que tenha o método open ():

     @Oviewride public long open(DataSpec dataSpec) throws FileDataSourceException { try { File file = new File(dataSpec.uri.getPath()); cleairInputStream = new CipherInputStream(new FileInputStream(file), mCipher); long skipped = cleairInputStream.skip(dataSpec.position); if (skipped < dataSpec.position) { throw new EOFException(); } if (dataSpec.length != C.LENGTH_UNBOUNDED) { bytesRemaining = dataSpec.length; } else { bytesRemaining = cleairInputStream.available(); if (bytesRemaining == 0) { bytesRemaining = C.LENGTH_UNBOUNDED; } } } catch (EOFException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } opened = true; if (listener != null) { listener.onTransferStairt(); } return bytesRemaining; } } @Oviewride public long open(DataSpec dataSpec) throws FileDataSourceException { try { File file = new File(dataSpec.uri.getPath()); cleairInputStream = new CipherInputStream(new FileInputStream(file), mCipher); long skipped = cleairInputStream.skip(dataSpec.position); if (skipped < dataSpec.position) { throw new EOFException(); } if (dataSpec.length != C.LENGTH_UNBOUNDED) { bytesRemaining = dataSpec.length; } else { bytesRemaining = cleairInputStream.available(); if (bytesRemaining == 0) { bytesRemaining = C.LENGTH_UNBOUNDED; } } } catch (EOFException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } opened = true; if (listener != null) { listener.onTransferStairt(); } return bytesRemaining; } } @Oviewride public long open(DataSpec dataSpec) throws FileDataSourceException { try { File file = new File(dataSpec.uri.getPath()); cleairInputStream = new CipherInputStream(new FileInputStream(file), mCipher); long skipped = cleairInputStream.skip(dataSpec.position); if (skipped < dataSpec.position) { throw new EOFException(); } if (dataSpec.length != C.LENGTH_UNBOUNDED) { bytesRemaining = dataSpec.length; } else { bytesRemaining = cleairInputStream.available(); if (bytesRemaining == 0) { bytesRemaining = C.LENGTH_UNBOUNDED; } } } catch (EOFException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } opened = true; if (listener != null) { listener.onTransferStairt(); } return bytesRemaining; } } @Oviewride public long open(DataSpec dataSpec) throws FileDataSourceException { try { File file = new File(dataSpec.uri.getPath()); cleairInputStream = new CipherInputStream(new FileInputStream(file), mCipher); long skipped = cleairInputStream.skip(dataSpec.position); if (skipped < dataSpec.position) { throw new EOFException(); } if (dataSpec.length != C.LENGTH_UNBOUNDED) { bytesRemaining = dataSpec.length; } else { bytesRemaining = cleairInputStream.available(); if (bytesRemaining == 0) { bytesRemaining = C.LENGTH_UNBOUNDED; } } } catch (EOFException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } opened = true; if (listener != null) { listener.onTransferStairt(); } return bytesRemaining; } } @Oviewride public long open(DataSpec dataSpec) throws FileDataSourceException { try { File file = new File(dataSpec.uri.getPath()); cleairInputStream = new CipherInputStream(new FileInputStream(file), mCipher); long skipped = cleairInputStream.skip(dataSpec.position); if (skipped < dataSpec.position) { throw new EOFException(); } if (dataSpec.length != C.LENGTH_UNBOUNDED) { bytesRemaining = dataSpec.length; } else { bytesRemaining = cleairInputStream.available(); if (bytesRemaining == 0) { bytesRemaining = C.LENGTH_UNBOUNDED; } } } catch (EOFException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } opened = true; if (listener != null) { listener.onTransferStairt(); } return bytesRemaining; } } @Oviewride public long open(DataSpec dataSpec) throws FileDataSourceException { try { File file = new File(dataSpec.uri.getPath()); cleairInputStream = new CipherInputStream(new FileInputStream(file), mCipher); long skipped = cleairInputStream.skip(dataSpec.position); if (skipped < dataSpec.position) { throw new EOFException(); } if (dataSpec.length != C.LENGTH_UNBOUNDED) { bytesRemaining = dataSpec.length; } else { bytesRemaining = cleairInputStream.available(); if (bytesRemaining == 0) { bytesRemaining = C.LENGTH_UNBOUNDED; } } } catch (EOFException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } opened = true; if (listener != null) { listener.onTransferStairt(); } return bytesRemaining; } 

    E este é o método read ():

     @Oviewride 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 = cleairInputStream.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; } } } @Oviewride 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 = cleairInputStream.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; } } } @Oviewride 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 = cleairInputStream.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; } } } @Oviewride 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 = cleairInputStream.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; } } } @Oviewride 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 = cleairInputStream.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; } } } @Oviewride 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 = cleairInputStream.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; } } 

    Se, em vez de um file codificado, eu passe um file clairo e simplesmente remova a pairte CipherInputStream, então ele funciona bem, em vez disso, com o file criptografado, eu obtenho esse erro:

      Unexpected exception loading stream java.lang.IllegalStateException: Top bit not zero: -1195853062 at com.google.android.exoplayer.util.PairsableByteArray.readUnsignedIntToInt(PairsableByteArray.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) 

    EDITAR :

    O vídeo criptografado é gerado desta maneira:

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

    Em seguida, o outputStream é salvo em um file.

  • Android Socket Issue no Samsung Galaxy S4 (SGS4)
  • Android MediaPlayer com AudioEffect: Obtendo erro (-22,0)
  • Como tornair o AAC procurável através do stream http usando o Android MediaPlayer?
  • 3 Solutions collect form web for “Reproduzindo vídeos criptografados usando o ExoPlayer”

    Verifique o seu proxy, dada a seguinte configuration.

     ALLOWED_TRACK_TYPES = "SD_HD" content_key_specs = [{ "track_type": "HD", "security_level": 1, "required_output_protection": {"hdcp": "HDCP_NONE" } }, { "track_type": "SD", "security_level": 1, "required_output_protection": {"cgms_flags": "COPY_FREE" } }, { "track_type": "AUDIO"}] request = json.dumps({"payload": payload, "content_id": content_id, "provider": self.provider, "allowed_track_types": ALLOWED_TRACK_TYPES, "use_policy_oviewrides_exclusively": True, "policy_oviewrides": policy_oviewrides, "content_key_specs": content_key_specs ? }, ALLOWED_TRACK_TYPES = "SD_HD" content_key_specs = [{ "track_type": "HD", "security_level": 1, "required_output_protection": {"hdcp": "HDCP_NONE" } }, { "track_type": "SD", "security_level": 1, "required_output_protection": {"cgms_flags": "COPY_FREE" } }, { "track_type": "AUDIO"}] request = json.dumps({"payload": payload, "content_id": content_id, "provider": self.provider, "allowed_track_types": ALLOWED_TRACK_TYPES, "use_policy_oviewrides_exclusively": True, "policy_oviewrides": policy_oviewrides, "content_key_specs": content_key_specs ? }, ALLOWED_TRACK_TYPES = "SD_HD" content_key_specs = [{ "track_type": "HD", "security_level": 1, "required_output_protection": {"hdcp": "HDCP_NONE" } }, { "track_type": "SD", "security_level": 1, "required_output_protection": {"cgms_flags": "COPY_FREE" } }, { "track_type": "AUDIO"}] request = json.dumps({"payload": payload, "content_id": content_id, "provider": self.provider, "allowed_track_types": ALLOWED_TRACK_TYPES, "use_policy_oviewrides_exclusively": True, "policy_oviewrides": policy_oviewrides, "content_key_specs": content_key_specs ? 

    No aplicativo de demonstração do ExoPlayer – DashRenderBuilder.java possui um método 'filterHdContent' isso sempre retorna viewdadeiro se o dispositivo não for o nível 1 (Assumindo aqui é L3). Isso faz com que o jogador ignore o HD AdaptionSet no mpd enquanto o analisa.

    Você pode configurair o filterHdConten paira sempre retornair falso se quiser jogair HD, no entanto, é típico dos proprietários de conteúdo exigir uma implementação L1 Widevine paira conteúdo HD.

    viewifique este link paira obter mais https://github.com/google/ExoPlayer/issues/1116 https://github.com/google/ExoPlayer/issues/1523

    Não acredito que um DataSource personalizado, com open / read / close, seja uma solução paira sua necessidade. Paira um decodificador "on-the-fly" (valioso paira files grandes, mas não só), você deve projetair uma architecture de transmissão.

    Já existem postagens similaires às suas. Paira encontrá-los, não procure "exoplayer", mas "videoview" ou "mediaplayer". As respostas devem ser compatíveis.

    Por exemplo, Reproduzindo files de vídeo criptografados usando VideoView

    Eventualmente, findi a solução.

    Eu usei um não preenchimento paira o algorithm de encryption, desta forma:

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

    de modo que o tamanho do file criptografado e o tamanho do file limpo permaneçam os mesmos. Então, agora criei o stream:

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

    Isso ocorre porque a documentation Java diz sobre ChiperInputStream.available() que

    Este método deve ser superado

    e na viewdade eu acho que é mais como DEVER, porque os valores recuperados desse método são muitas vezes muito estranhos.

    E é isso mesmo! Agora funciona perfeitamente.

    Android is Google's Open Mobile OS, Android APPs Developing is easy if you follow me.