일상&개발 로그

MediaDataSource를 이용한 프로그레시브 재생 구현 본문

개발/안드로이드 개발

MediaDataSource를 이용한 프로그레시브 재생 구현

dskim98 2017. 8. 3. 18:47

Android에서 제공하는 기본 MediaPlayer는 Uri, FilePath, FileDescriptor만 dataSource로 지정 가능 했습니다.

또 내부로직이 거의 native로 구현되어 reflection의 여지도 없었습니다.

그래서 다운로드와 동시에 재생하는 버퍼링을 외부에서 구현할 수 없었습니다.

(setDataSource를 이용하면 MediaPlayer내부에서 버퍼링하긴 합니다.)

MarshMallow부터 제공되는 MediaDataSource를 이용하면 외부에서도 버퍼링처럼 동작하도록 구현이 가능합니다.

프로그레시브 다운로드 정보 링크

(주의: 제공되는 음원포맷이 프로그레시브 다운로드를 지원해야합니다.)


@RequiresApi(api = Build.VERSION_CODES.M)

public class BufferMediaDataSource extends MediaDataSource {

    private volatile byte[] mBuffer;    // byte array for whole media

    private int fileSize;

    private int writeIndex;

    private BufferListener listener;


    public BufferMediaDataSource(long fileSize, BufferListener listener) {

        this.fileSize = Integer.parseInt(String.valueOf(fileSize));

        mBuffer = new byte[this.fileSize];

        this.listener = listener;

    }


    public void inputData(byte[] data, int length) {

        if (mBuffer != null) {

            System.arraycopy(data, 0, mBuffer, writeIndex, length);

            writeIndex += length;

        }

    }


    @Override

    public int readAt(long position, byte[] buffer, int offset, int size) throws IOException {

        if(position == writeIndex) {

            return -1;

        }

        if(position + size > writeIndex) {

            if(fileSize == writeIndex) {

                int rest = (int) (writeIndex - position);

                System.arraycopy(mBuffer, (int) position, buffer, offset, rest);

                return rest;

            } else {

// loading data is faster than downloading data.

try {

Thread.sleep(300);    // wait a second for downloading.

// or MediaPlayer.pause();

} catch (InterruptedException e) {

e.printStackTrace();

}

            }

        }

        if (mBuffer != null) {

            System.arraycopy(mBuffer, (int) position, buffer, offset, size);

            return size;

        }

        return 0;

    }


    @Override

    public long getSize() throws IOException {

        return fileSize;

    }


    @Override

    public void close() throws IOException {

        mBuffer = null;

    }


}

MediaPlayer가 reatAt을 통해 byte[]를 계속 읽어가기 때문에 inputData를 통해 mBuffer를 계속 채워줘야 합니다.

readAt의 파라미터 중 position은 읽기 시작하는 위치, buffer는 MediaPlayer가 읽을 버퍼, offset은 offset, size는 버퍼 크기입니다.


유의할 점은 읽어야 하는 위치가 아직 준비되지 않았을 경우 MediaPlayer를 pause시켜야 하고 마지막으로 읽어갈 사이즈는 기존 버퍼 사이즈보다 작기 때문에 읽어가는 값 만큼 return을 해줘야 합니다.




Comments