2022. 1. 5. 17:24ㆍMultimedia
- 목차
ExoPlayer의 decoding
ExoPlayer는 application side의 multimedia framework이다. Android native framework에 포함되지는 않는다. 즉, ExoPlayer는 기본적으로 H/W accelerated decoder를 사용하는 부분을 포함하지 않는다.
ExoPlayer 지원 Extractor
ExoPlayer의 extractor에는 다음과 같은 것들이 있다. MP4를 기본으로 지원하고 있음을 알 수 있다.
이외에도 Flac, Jpeg, Amr, Wav, Ogg, Flv, Ps, Hls, WebTT extractor 등이 존재한다.
ExoPlayer의 codec
위 그림에서 볼 수 있듯이 ExoPlayer 내 MediaCodecRenderer 부분이 native framework의 MediaCodec과의 접점이다. AsynchronousMediaCodecAdapter가 MediaCodec으로 decode 요청을 수행하여 decode가 이뤄지게 된다.
ExoPlayer에서 특정 codec을 decode를 수행하는데는 다음과 같은 경우들이 존재한다.
코덱이 ExoPlayer가 지원하는 extractor에서 지원해야 함 + MediaCodec I/F를 통한 Native framework에서 codec을 지원해야 함(S/W이던, H/W이던)
extractor들이 지원하지 않는 content를 지원하고자 한다면, Extractor를 base로 하여 신규 Extractor를 별도 개발 해야 한다.
static const struct VideoCodingMapEntry {
const char *mMime;
OMX_VIDEO_CODINGTYPE mVideoCodingType;
} kVideoCodingMapEntry[] = {
{ MEDIA_MIMETYPE_VIDEO_AVC, OMX_VIDEO_CodingAVC },
{ MEDIA_MIMETYPE_VIDEO_HEVC, OMX_VIDEO_CodingHEVC },
{ MEDIA_MIMETYPE_VIDEO_MPEG4, OMX_VIDEO_CodingMPEG4 },
{ MEDIA_MIMETYPE_VIDEO_H263, OMX_VIDEO_CodingH263 },
{ MEDIA_MIMETYPE_VIDEO_MPEG2, OMX_VIDEO_CodingMPEG2 },
{ MEDIA_MIMETYPE_VIDEO_VP8, OMX_VIDEO_CodingVP8 },
{ MEDIA_MIMETYPE_VIDEO_VP9, OMX_VIDEO_CodingVP9 },
{ MEDIA_MIMETYPE_VIDEO_DOLBY_VISION, OMX_VIDEO_CodingDolbyVision },
{ MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC, OMX_VIDEO_CodingImageHEIC },
{ MEDIA_MIMETYPE_VIDEO_AV1, OMX_VIDEO_CodingAV1 },
};
SoftOMXPlugin
신규로 codec을 개발하여 추가하고자 한다면, SoftOMXPlugin과 같이 개발한 codec에 대한 OMX plugin을 등록하는 부분의 코드도 수정해 줘야 한다.
static const struct {
const char *mName;
const char *mLibNameSuffix;
const char *mRole;
} kComponents[] = {
{ "OMX.google.aac.decoder", "aacdec", "audio_decoder.aac" },
{ "OMX.google.aac.encoder", "aacenc", "audio_encoder.aac" },
{ "OMX.google.amrnb.decoder", "amrdec", "audio_decoder.amrnb" },
{ "OMX.google.amrnb.encoder", "amrnbenc", "audio_encoder.amrnb" },
{ "OMX.google.amrwb.decoder", "amrdec", "audio_decoder.amrwb" },
{ "OMX.google.amrwb.encoder", "amrwbenc", "audio_encoder.amrwb" },
{ "OMX.google.h264.decoder", "avcdec", "video_decoder.avc" },
{ "OMX.google.h264.encoder", "avcenc", "video_encoder.avc" },
{ "OMX.google.hevc.decoder", "hevcdec", "video_decoder.hevc" },
{ "OMX.google.g711.alaw.decoder", "g711dec", "audio_decoder.g711alaw" },
{ "OMX.google.g711.mlaw.decoder", "g711dec", "audio_decoder.g711mlaw" },
{ "OMX.google.mpeg2.decoder", "mpeg2dec", "video_decoder.mpeg2" },
{ "OMX.google.h263.decoder", "mpeg4dec", "video_decoder.h263" },
{ "OMX.google.h263.encoder", "mpeg4enc", "video_encoder.h263" },
{ "OMX.google.mpeg4.decoder", "mpeg4dec", "video_decoder.mpeg4" },
{ "OMX.google.mpeg4.encoder", "mpeg4enc", "video_encoder.mpeg4" },
{ "OMX.google.mp3.decoder", "mp3dec", "audio_decoder.mp3" },
{ "OMX.google.vorbis.decoder", "vorbisdec", "audio_decoder.vorbis" },
{ "OMX.google.opus.decoder", "opusdec", "audio_decoder.opus" },
{ "OMX.google.vp8.decoder", "vpxdec", "video_decoder.vp8" },
{ "OMX.google.vp9.decoder", "vpxdec", "video_decoder.vp9" },
{ "OMX.google.vp8.encoder", "vpxenc", "video_encoder.vp8" },
{ "OMX.google.vp9.encoder", "vpxenc", "video_encoder.vp9" },
{ "OMX.google.raw.decoder", "rawdec", "audio_decoder.raw" },
{ "OMX.google.flac.decoder", "flacdec", "audio_decoder.flac" },
{ "OMX.google.flac.encoder", "flacenc", "audio_encoder.flac" },
{ "OMX.google.gsm.decoder", "gsmdec", "audio_decoder.gsm" },
};
OMX plugin 생성 요청 시 다음의 method가 실행되어 OMX plugin instance를 생성하게 된다.
OMX_ERRORTYPE SoftOMXPlugin::makeComponentInstance(
const char *name,
const OMX_CALLBACKTYPE *callbacks,
OMX_PTR appData,
OMX_COMPONENTTYPE **component) {
ALOGV("makeComponentInstance '%s'", name);
...
decode가 완료된 buffer는 MediaCodecVideoRenderer에 의해 화면으로 rendering 되게 된다. 이 때 Native framework이 제공하는 Surface interface를 사용하여 해당 과정을 수행하게 된다.
MediaCodecRenderer는 AsynchronousMediaCodecAdapter을 통해 decode된 surface를 MediaCodecVideoRenderer를 통해 화면으로 출력하게 되며, 마찬가지로 decode된 audio data를 (PCM data) AudioTrack을 통해서 AudioFlinger로 전달하게 된다.
rendering이 수행될 surface는 application side에서 설정하게 된다. ExoPlayer demo application에서는 다음과 같이 Surface를 생성하여 SimpleExoPlayer에 설정하게 된다.
SimpleExoPlayer player = new SimpleExoPlayer.Builder(getApplicationContext()).build();
player.setMediaSource(mediaSource);
player.prepare();
player.play();
player.setRepeatMode(Player.REPEAT_MODE_ALL);
surfaceControl =
new SurfaceControl.Builder()
.setName(SURFACE_CONTROL_NAME)
.setBufferSize(/* width= */ 0, /* height= */ 0)
.build();
videoSurface = new Surface(surfaceControl);
player.setVideoSurface(videoSurface);
VideoProcessingGLSurfaceView.java를 보면, GL surface를 설정하는 코드도 확인해 볼 수 있다.
private void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture) {
mainHandler.post(
() -> {
SurfaceTexture oldSurfaceTexture = this.surfaceTexture;
Surface oldSurface = VideoProcessingGLSurfaceView.this.surface;
this.surfaceTexture = surfaceTexture;
this.surface = new Surface(surfaceTexture);
releaseSurface(oldSurfaceTexture, oldSurface);
if (videoComponent != null) {
videoComponent.setVideoSurface(surface);
}
});
}
...
@Override
public synchronized void onSurfaceCreated(GL10 gl, EGLConfig config) {
texture = GlUtil.createExternalTexture();
surfaceTexture = new SurfaceTexture(texture);
surfaceTexture.setOnFrameAvailableListener(
surfaceTexture -> {
frameAvailable.set(true);
requestRender();
});
onSurfaceTextureAvailable(surfaceTexture);
}
최초 frame의 rendering 시 SimpleExoPlayer의 addVideoListener를 통해 설정된 listener callback이 호출되게 되며, 여기서 thumbnail image를 보여 주다가 비디오 frame의 rendering으로 전환 시키는 등의 처리를 할 수 있다.
simpleExoPlayer.addVideoListener(object : VideoListener {
override fun onRenderedFirstFrame() {
super.onRenderedFirstFrame()
binding.thumbnailView.visibility = View.GONE
}
})
'Multimedia' 카테고리의 다른 글
MP4 container (0) | 2022.04.21 |
---|---|
영상 품질 측정 기법 (0) | 2022.04.16 |
ExoPlayer: master/media playlist parsing (0) | 2021.11.16 |
ExoPlayer build & streaming media source 생성 (0) | 2021.11.14 |
ExoPlayer 동작 분석 (prepare) (0) | 2021.11.14 |