레이블이 MediaCodec인 게시물을 표시합니다. 모든 게시물 표시
레이블이 MediaCodec인 게시물을 표시합니다. 모든 게시물 표시

2012년 12월 19일 수요일

Jelly Bean Media Codec API 사용 기록

Jelly Bean 부터 Media Codec API 를 사용할 수 있게 되었습니다.

1. 소개
미디어 코덱을 이용해서 RIMM 파일을 재생하는 플레이어를 만들어 보았습니다.
그 과정에서 이해하기 힘들었던 점을 기록하겠습니다.
*) 참고로 플레이어 제작 경험 없이 단편적으로 경험한 문제들만 다룹니다.
이전에 플레이어를 만들어 본 경험 없이 적용한 것이라 블로그 내용을 채우는데 시간이 오래 걸릴 수 있습니다.

안드로이드 소스 코드를 확인해 보면 Media Codec API 가 추가되었다기 보다는 기존에 존재하는 API 를 일반 사용자들이 사용할 수 있게 공개했다는 성격이 더 강한 것으로 생각합니다.

아래 페이지에 예제가 있지만 정보가 너무 부족합니다.
http://developer.android.com/reference/android/media/MediaCodec.html

필연적으로 안드로이드 소스 코드를 확인해볼 필요가 있습니다.
Awesome Player 라는 키워드로 소스 코드를 검색해 보시기 바랍니다.

2. 주요 클래스 소개

 - MediaCodec
추가된 API 는 encoding 된 media 데이터를 decoding 해서 출력 가능한 형태로 변환해 주는 MediaCodec 이 있습니다.

 - MediaExtractor
Media 를 저장하는 container 행태 (mp4 등) 로 부터 media 에 대한 메타 정보를 추출하고 chunk 단위로 데이터를 추출해 주는 MediaExtrator 가 있습니다.

MediaExtractor 를 이용하게 되면 mp4 파일 등으로 부터 MediaCodec 을 생성하는 데 필요한 MediaFormat 객체를 손쉽게 얻을 수 있습니다.

만약 안드로이드에서 지원하지 않는 종류의 container 를 재생하고자 한다면 제일 핵심은 MediaCodec 을 생성하는데 필요한 MediaFormat 을 만드는 일일 겁니다.

3. 개발 경험

경험1) 오디오 출력
AAC 오디오를 재생해야 하는 상황이 있었습니다.

http://developer.android.com/reference/android/media/MediaCodec.html
위 링크의 예제대로 하면 에러가 발생했습니다.

Media codec 에 대한 무지로 인해 해법을 찾는 데 더 오랜 시간이 걸렸습니다.
구글로 찾은 AAC 에 대한 자료와 안드로이드 소스를 확인한 끝에 오디오 데이터에 대한 정보를 담는 2 바이트 크기의 descriptor 역할을 하는 데이터에 대해 알게 되었습니다.

Audio Specific Config
 - http://wiki.multimedia.cx/index.php?title=MPEG-4_Audio#Audio_Specific_Config

MediaFormat 객체에 csd-0 라는 key 이름으로 위 데이터를 직접 입력해 주셔야 합니다.
예)
MediaFormat audioFormat = MediaFormat.createAudioFormat("audio/mp4a-latm", 32000, 1);

ByteBuffer bb = ByteBuffer.allocateDirect(2);
bb.put(new byte[] {(byte)0x12, (byte)0x88});
bb.flip();
audioFormat.setByteBuffer("csd-0", bb);


경험2) 비디오 출력
AVC 비디오를 재생해야 하는 상황이 있었습니다.
AAC 오디오의 경우 처럼 MediaFormat 을 만들 때 따로 descriptor 를 집어 넣어 줄 필요는 없었습니다.
코덱에 대한 무지로 인해 정확히는 모르겠지만 RIMM 포맷의 경우에는 스트림 초반에 AVC 비디오에 대한 Config frame 이 존재하였습니다.
해당 config frame 을 Media Codec 의 input buffer 에 넣어 주니 정상적으로 재생을 하였습니다.

queueInputBuffer() 함수를 이용해 config frame 을 넣어주면서 flag 를 MediaCodec.BUFFER_FLAG_CODEC_CONFIG 로 입력해 줍니다.

경험 3) AV sync 문제
이번 MediaCodec API 에는 재생 부분 까지는 지원을 해주지 않는 것으로 보입니다. (단지 제가 찾지 못하는 경우 일 수도 있습니다.)
그렇기 때문에 재생에 관한 부분 AV sync 등의 문제는 스스로 해결을 해야 했습니다.

재생에 있어서 가장 중요한 값은 PTS 입니다.
PTS 란 해당 비디오 또는 오디오 프레임을 출력하는 시간입니다.
PTS 값은 절대값 또는 상대값일 수 있으며 시간 단위도 미디어 포맷별로 다양할 것입니다.
하지만 어떤 포맷이든 AV 싱크를 위한 값이 없을 수는 없다고 봅니다.

!) 사람은 비디오보다 오디오 재생에 민감합니다.
비디오 재생이 조금 원활하지 않은 경우 인식하지 못하거나 무시하고 넘어가는 경우가 많습니다.
하지만 오디오 재생이 조금만 이상해도 금새 인식할 수 있습니다.
그래서 오디오 재생을 우선하고 비디오 재생 속도를 맞추도록 개발했습니다.

경험 4) 삼성 갤럭시 류의 폰들
삼성 갤럭시 S3, 갤럭시 노트2 JB 폰에서 mono channel audio 를 decode 하고 재생하는데 소리가 늘어지는 현상이 있었습니다.

소스로 들어온 오디오의 channel count 가 하나 (mono) 인데 codec 으로 decode 되는 결과물의 channel count 가 두개 (stereo) 였습니다.

기존에는 소스로 들어온 오디오 데이터의 정보를 토대로 AudioTrack 을 생성했는데 위 현상 확인 후 MediaCodec.INFO_OUTPUT_FORMAT_CHANGED 이벤트가 발생했을 때 넘어오는 mediaformat 정보를 토대로 AudioTrack 을 생성하게 수정하였습니다.