마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

Exoplayer를 사용해 암호화된 동영상 파일을 스트리밍 하고싶습니다.

0 추천

 

저는 지금 저만의 로직으로 완성된 암호화된 동영상을 재생하려합니다. 복호화된 파일 생성하고 재생하기에는 동영상의 용량이 너무나 크기 때문에 재생될 까지 시간이 너무 많이 걸립니다. 그래서 찾아낸 것이 ExoPlayer 사용해서 InputStream으로 복호화하면서 재생하는 방법이죠

그리고 제가 생각한 기능을 완성할 흐름은 다음과 같습니다

1. ExoPlayer UI 완성한다

2. 나의 암호화 로직을 사용해 다운로드된 파일을 암호화한다.

3. InputStream으로 파일을 받아오면서 동시에 복호화하며 재생한다. (스트리밍)

1번과 2번까지는 어떻게든 해보겠지만 3번은 저에게 매우 어렵습니다. 혹시 구체적인 코드와 설명을 알려주실수 있으신분이 계신가요. 혹시 아시는 분은 혜안을 부탁드립니다. 감사합니다.

 

 try {
        ios = new FileInputStream(params[0]);
        fos = context.openFileOutput(params[1] + ".mp4", MODE_PRIVATE);

        ScatteringByteChannel sbc = ios.getChannel();
        GatheringByteChannel gbc = fos.getChannel();

        File file = new File(params[0]);
        fileLength = file.length();

        startTime = System.currentTimeMillis();
        int read = 0;
        readb = 0;
        ByteBuffer bb = ByteBuffer.allocate(4096);
        while ((read = sbc.read(bb)) != -1) {
            bb.flip();
            gbc.write(ByteBuffer.wrap(enDecryptVideo.combineByteArray(bb.array())));
            bb.clear();
            readb += read;
            if (readb % (4096  * 1024 * 3) == 0){
                publishProgress(((int) ( readb * 100 / fileLength)));
            } else if (readb == fileLength) {
                publishProgress(101);
            }
        }



        ios.close();
        fos.close();
    } catch (Exception e) {
        e.getMessage();
    } finally {
        Log.d(TAG, "doInBackground: " + (System.currentTimeMillis() - startTime));
    }

 위의 코드는 복호화 된 파일을 만들 때 사용한 코드입니다. 이제 복호화와 동시에 재생해야합니다. 파일을 만들지는 않습니다. 

준버기 (1,160 포인트) 님이 2018년 7월 16일 질문
준버기님이 2018년 7월 17일 수정

1개의 답변

+1 추천
 
채택된 답변

아래코드는 적어주신 코드를 기반으로 

4096 byte단위로 복호화한 데이터를 생성하여,  read시 복호화 된 데이터를 반환하도록 한 코드입니다.

이 코드를 참조해서,

 exoplayer에서 파일을 읽은 dataSource의 InputStream 부분을 이 클래스로 대체 해 주시면,

 원하시는대로 복호화(?) 되면서 재생 될 겁니다.

public class CustomInputStream extends InputStream {
    private long fileLength;
    private ByteBuffer bb;
    ScatteringByteChannel sbc ;
    private long read;
    private ByteBuffer encBuffer;
    public CustomInputStream(String filename) throws FileNotFoundException {
        this(new File(filename));
    }

    public CustomInputStream(File file) throws FileNotFoundException {
        FileInputStream ios = new FileInputStream(file);
        fileLength =file.length();
        bb = ByteBuffer.allocate(4096);
        sbc = ios.getChannel();
        read = 0;
    }


    @Override
    public int read() throws IOException {
        if(read == 0) { // 미리 읽어둔 데이터가 없을 경우
            read = sbc.read(bb);  // 파일에서 데이터를 읽어
            encBuffer = ByteBuffer.wrap(enDecryptVideo.combineByteArray(bb.array())); // 복호화 처리
        }

        if(read > 0) { // 복호화 된 데이터가 있을 경우 
            read-=1; // 한 바이트 반환하면서 리드 사이즈를 1 줄임
            return encBuffer.get(); // 복호화 된 데이터를 한 바이트 반환
        } else {
            return -1; // EOS;
        }
    }
}
익명사용자 님이 2018년 7월 17일 답변
준버기님이 2018년 7월 17일 채택됨
정말 감사합니다. 헌데 datsource에 inputstream클래스를 찾지 못하였습니다
inputstream용 DataSource를 만들어주시면 되죠....
뭐 아래 같이  DataSource를 한개 만들어서 대체 해 주셔도 되고요.

public class CustomDataSource implements DataSource {
    private long fileLength;
    private ByteBuffer bb;
    ScatteringByteChannel sbc ;
    private long read;
    private ByteBuffer encBuffer;
    private Uri uri;
    @Override
    public long open(DataSpec dataSpec) throws IOException {
        FileInputStream ios = new FileInputStream(dataSpec.uri.toString());
        fileLength =dataSpec.length;
        bb = ByteBuffer.allocate(4096);
        sbc = ios.getChannel();
        read = 0;
        uri = dataSpec.uri;
        return fileLength;
    }

    public int read() throws IOException {
        if(read == 0) { // 미리 읽어둔 데이터가 없을 경우
            read = sbc.read(bb);  // 파일에서 데이터를 읽어
            encBuffer = ByteBuffer.wrap(enDecryptVideo.combineByteArray(bb.array())); // 복호화 처리
        }

        if(read > 0) { // 복호화 된 데이터가 있을 경우
            read-=1; // 한 바이트 반환하면서 리드 사이즈를 1 줄임
            return encBuffer.get(); // 복호화 된 데이터를 한 바이트 반환
        } else {
            return -1; // EOS;
        }
    }

    @Override
    public int read(byte[] b, int off, int len) throws IOException {
        if (b == null) {
            throw new NullPointerException();
        } else if (off < 0 || len < 0 || len > b.length - off) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return 0;
        }

        int c = read();
        if (c == -1) {
            return -1;
        }
        b[off] = (byte)c;

        int i = 1;
        try {
            for (; i < len ; i++) {
                c = read();
                if (c == -1) {
                    break;
                }
                b[off + i] = (byte)c;
            }
        } catch (IOException ee) {
        }
        return i;
    }

    @Override
    public Uri getUri() {
        return uri;
    }

    @Override
    public void close() throws IOException {
        if(sbc != null) {
            sbc.close();
        }
        sbc = null;
    }

}
혹시 관련 질문 확인해 주실수 있으신가요.
후! 이 코드로 원본 영상도 돌려봤는데 재생이 안되는군요 ㅠㅠ
원본코드로 원본 영상은 돌아가나요? 만약 돌아간다면, 복호화 쪽이 잘 못 되었으니. 만들어드린 코드 참조해서, 복호화 코드를 다시 구성해보세요.. 안된다면, 컨텐츠 코덱/컨테이너를 exoplayer에서 지원 못 하는거니, 원본 컨텐츠를 돌아가는 포멧으로 바꾸셔야 할 겁니다.
감사합니다 많은 도움 되었습니다. 항상 감사합니다!
...