Подтвердить что ты не робот

Воспроизведение зашифрованного видео с помощью ExoPlayer

Я использую ExoPlayer в Android, и я пытаюсь воспроизвести зашифрованное видео, хранящееся локально.

Модульность ExoPlayer позволяет создавать пользовательские компоненты, которые могут быть введены в ExoPlayer, и это похоже на случай. Действительно, после некоторых исследований я понял, что для достижения этой задачи я мог бы создать собственный DataSource и переопределить open(), read() и close().

Я также нашел это решение, но на самом деле здесь весь файл расшифровывается за один шаг и сохраняется в ясном входном потоке. Это может быть хорошо во многих ситуациях. Но что, если мне нужно воспроизвести большой файл?

Поэтому возникает вопрос: как я могу воспроизвести зашифрованное видео в ExoPlayer, расшифровать содержимое "на лету" (без дешифрования всего файла)? Возможно ли это?

Я попытался создать пользовательский DataSource, который имеет метод open():

@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, тогда он работает нормально, вместо этого с зашифрованным файлом я получаю эту ошибку:

    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:

зашифрованное видео генерируется таким образом:

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);

Затем выходной поток сохраняется в файл.

4b9b3361

Ответ 1

В конце концов я нашел решение.

Я использовал no-padding для алгоритма шифрования, таким образом:

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

чтобы размер зашифрованного файла и размер прозрачного файла остались прежними. Итак, теперь я создал поток:

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

Это связано с тем, что документация Java говорит о ChiperInputStream.available(), что

Этот метод должен быть отменен

и на самом деле я думаю, что это больше похоже на MUST, потому что значения, полученные из этого метода, часто очень странны.

И это все! Теперь он отлично работает.

Ответ 2

Я не верю, что пользовательский DataSource, с открытым/прочитанным/закрытым, является решением вашей потребности. Для дешифрования "на лету" (ценного для больших файлов, но не только) вы должны создать поточную архитектуру.

Уже есть сообщения, похожие на ваши. Чтобы найти их, не смотрите "exoplayer", а "videoview" или "mediaplayer". Ответы должны быть совместимы.

Например, Воспроизведение зашифрованных видеофайлов с помощью VideoView

Ответ 3

проверьте свой прокси-сервер, учитывая следующую конфигурацию.

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_overrides_exclusively": True,
                      "policy_overrides": policy_overrides,
                      "content_key_specs": content_key_specs
                     ?

В демонстрационном приложении ExoPlayer - DashRenderBuilder.java имеет метод filterHdContent, это всегда возвращает true, если устройство не является уровнем 1 (предположим, что здесь L3). Это заставляет игрока игнорировать HD AdaptionSet в mpd, анализируя его.

Вы можете установить, что filterHdContent всегда возвращает false, если вы хотите воспроизвести HD, однако для владельцев контента типично требовать реализацию L1 Widevine для контента HD.

проверить эту ссылку для более https://github.com/google/ExoPlayer/issues/1116 https://github.com/google/ExoPlayer/issues/1523

Ответ 4

@GVillani82: - У меня похожая проблема. Я зашифровал видео, хранящиеся на Amazon AWS (Amazon s3). Я не хочу загружать зашифрованный файл на свой мобильный телефон, но мне нужно расшифровать данные на лету и показать их на exoplayer. Я видел похожий код, который показан выше. Но я все еще не понимаю механизм открытия и чтения.

У меня есть URL к Amazon S3 Videos и ключу шифрования. Мне нужна ваша помощь в создании архитектуры для этого требования. Пожалуйста помоги