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

파일 복호화와 동시에 스트리밍 할 때, 멀티스레딩에 대한 질문입니다.

0 추천

* 저는 지금 파일을 복호화해서 재생하는 기능을 만들고 있습니다. Http 프록시 로컬 서버를 마련해 두고 파일을 복호화 하면서 동시에 스트리밍하고 있는데요

* 문제는 다음과 같습니다. 파일 복호화가 완료되지 않으면 플레이어의 SeekBar 탐색을 수가 없습니다. Skip 되지 않는 것인데요. 지금은 프로그레스를 띄어서 복호화가 완료되기 전까지는 Seekbar 사용할 없도록 했습니다. 계속 이런식으로 하다간 사용자 저항이 심할게 뻔합니다. 특히 대용량 동영상 파일이라면 확실하죠...

* 그래서 제가 생각한 방식은 복호화를 최대한 빠르게하자 입니다. 복호화를 담당하는 쓰레드를 여러개 두고 분할 복호화를 하는 것이죠. 마지막엔 물론 파일을 합쳐야 합니다. 파일의 순서대로 4096 바이트 마다 복호화를 해야합니다. 그리고 스트리밍을 해야 하기 때문에 복호화 하면서 재생이 되야하는데 절차가 복잡하네요. 멀티스레드로 돌렸을 때도 스트리밍이 될까요..  방식이 과연 제일 효율적일까요

* 이 문제를 해결할 가장 좋은 방식은 무엇일까요? (ExoPlayer제외)

준버기 (1,160 포인트) 님이 2018년 7월 19일 질문
복호화 스레드와 서버 스레드를 만드는 코드입니다.
public class VideoDownloadAndPlayService
{
    private static final String TAG = "VideoDownloadAndPlaySer";
    private static LocalFileStreamingServer server;
    private static VideoDownloader videoDownloader;

    private VideoDownloadAndPlayService(LocalFileStreamingServer server)
    {
        this.server = server;
    }

    public static VideoDownloadAndPlayService startServer(final Activity activity, EnDecryptVideo enDecryptVideo, String videoUrl, String videoId, String pathToSaveVideo, final String ipOfServer, final VideoStreamInterface callback)
    {
        videoDownloader = (VideoDownloader) new VideoDownloader(activity, enDecryptVideo).execute(videoUrl, videoId);
        server = new LocalFileStreamingServer(new File(pathToSaveVideo, videoId + ".mp4"));
        server.setSupportPlayWhileDownloading(true);
        new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                server.init(ipOfServer);

                activity.runOnUiThread(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        server.start();
                        callback.onServerStart(server.getFileUrl());
                    }
                });
            }
        }).start();

        return new VideoDownloadAndPlayService(server);
    }

    public void start(){
        server.start();
    }

    public void stop(){
        server.stop();
    }

    public void stopDownloader() { videoDownloader.cancel(true); }

    public static interface VideoStreamInterface{
        public void onServerStart(String videoStreamUrl);
    }
}
다음은 복호화 하는 스레드 입니다.
public class VideoDownloader extends AsyncTask<String, Integer, Void> {

    private static final String TAG = "VideoDownloader";
    public static final int DATA_READY = 1;
    public static final int DATA_NOT_READY = 2;
    public static final int DATA_CONSUMED = 3;
    public static final int DATA_NOT_AVAILABLE = 4;
    private Context context;
    private EnDecryptVideo enDecryptVideo;

    public static int dataStatus = -1;

    public VideoDownloader(Context context, EnDecryptVideo enDecryptVideo) {
        Log.d(TAG, "VideoDownloader: " + "Async Ready");
        this.context = context;
        this.enDecryptVideo = enDecryptVideo;
    }

    public static boolean isDataReady() {
        dataStatus = -1;
        boolean res = false;
        if (fileLength == readb) {
            dataStatus = DATA_CONSUMED;
            res = false;
        } else if (readb > consumedb) {
            dataStatus = DATA_READY;
            res = true;
        } else if (readb <= consumedb) {
            dataStatus = DATA_NOT_READY;
            res = false;
        } else if (fileLength == -1) {
            dataStatus = DATA_NOT_AVAILABLE;
            res = false;
        }
        return res;
    }

    /**
     * Keeps track of read bytes while serving to video player client from server
     */
    public static int consumedb = 0;

    /**
     * Keeps track of all bytes read on each while iteration
     */
    private static long readb = 0;

    /**
     * Length of file being downloaded.
     */
    static long fileLength = -1;

    @Override
    protected Void doInBackground(String... params) {
        long startTime = 0;
        FileInputStream ios = null;
        FileOutputStream fos = null;

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

        return null;
    }

    @Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);
        int progress = values[0];
        ((VideoActivity)context).onProgressResult(values[0]);
        Log.d(TAG, "onProgressUpdate: " + progress);

    }

    @Override
    protected void onPostExecute(Void aVoid) {
        super.onPostExecute(aVoid);
        Log.w("download", "Done");
    }
}

답변 달기

· 글에 소스 코드 보기 좋게 넣는 법
· 질문에 대해 추가적인 질문이나 의견이 있으면 답변이 아니라 댓글로 달아주시기 바랍니다.
표시할 이름 (옵션):
개인정보: 당신의 이메일은 이 알림을 보내는데만 사용됩니다.
스팸 차단 검사:
스팸 검사를 다시 받지 않으려면 로그인하거나 혹은 가입 하세요.
...