ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [ExoPlayer] Android로 음악 플레이어 만들기
    카테고리 없음 2020. 12. 19. 21:37

     

    안녕하세요, 코드저장소 알락입니다.

    이번에 프로젝트를 하나 진행하면서 안드로이드 환경에서 음악 플레이어를 구현하게 되었습니다. 밑단부터 플레이어를 제작하기에는 시간이 촉박해서 이미 잘 만들어져있는 오픈소스를 찾고 있었는데, ExoPlayer가 눈에 띄었어요. 사용하다보니 괜찮은 오픈소스 플레이어인 것 같아 블로그에 소개드리려고 준비했습니다.


    ExoPlayer는 안드로이드의 어플리케이션 수준 미디어 플레이어 입니다. 안드로이드의 기존 MediaPlayer API를 대체하여 로컬 영역이나 인터넷에서의 오디오와 비디오를 재생할 수 있습니다. ExoPlayer는 현재 안드로이드 MediaPlayer API가 제공을 하고 있지 않는 기능들을 제공하고 있습니다(DASH, SmoothStreaming). MediaPlayer API와는 다르게 ExoPlayer는 맞춤화와 확장성이 용이하고, 플레이 스토어 어플리케이션의 업데이트와 같이 업데이트될 수 있습니다.

    [원문번역][참고]http://exoplyaer.dev

     

     

    ExoPlayer 모듈 추가하기

    일단 HelloWorld Project부터 진행해보겠습니다. 목표는 안드로이드 프로젝트에서 노래 하나를 재생해보는 것입니다.

    안드로이드에 ExoPlayer 종속성(Dependency)에 추가하도록 하겠습니다.

    안드로이드 프로젝트 생성하면 프로젝트 구성요소 중 Gradle 가 있습니다.

     

    거기서 build.gradle 에서 밑에 해당되는 작업을 합니다.

    repositories {
        google()
        jcenter()
    }

     

    그리고 다른 build.gradle 에서 밑에 해당되는 작업을 합니다.

    dependencies {
    
    	//추가해야 할 내용
        implementation 'com.google.android.exoplayer:exoplayer-core:2.X.X'
        implementation 'com.google.android.exoplayer:exoplayer-dash:2.X.X'
        implementation 'com.google.android.exoplayer:exoplayer-ui:2.X.X'
        
    }

     

    위 두 작업을 통해서 이제 안드로이드 프로젝트에서 ExoPlayer가 지원하는 라이브러리를 사용할 수 있습니다. 이 밖에 추가하여 사용가능한 기능은 다음과 같습니다.

     

    • exoplayer-core : 코어 기능 지원(필수)
    • exoplayer-dash : DASH 컨텐츠 지원
    • exoplayer-hls : HLS 컨텐츠 지원
    • exoplayer-smoothstreaming : SmoothStreaming 컨텐츠 지원
    • exoplayer-ui : ExoPlayer 사용을 위한 UI 컴포넌트와 리소스

     

    플레이어 만들기

    우선은 제일 기본적인 플레이어를 만들어보려고 합니다. ExoPlayer가 제공하는 기본 UI를 만들어주기 위해 MainActivity의 Layout으로 가봅시다.

     

    <com.google.android.exoplayer2.ui.PlayerControlView
        android:id="@+id/main_pcv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:show_timeout="0"
        app:layout_constraintBottom_toBottomOf="parent" />

     

    기본적인 ExoPlayer의 플레이어 UI 입니다. 위 코드를 추가하면 '이전 노래', '다음 노래', '되감기', '빨리감기', '재생', '일시정지'의 UI가 별다른 추가 사항 없이 한꺼번에 화면에 출력되게 합니다.

     

    안드로이드 MainActivity를 찾아가봅시다. 다음의 코드를 통해 SimpleExoPlayer를 선언하고 플레이어에 대한 인스턴스를 생성할 있습니다.

     

    SimpleExoPlayer player = new SimpleExoPlayer.Builder(this).build();
    PlayerControlView pcv = findViewById(R.id.main_pcv);

     

    이제 플레이어 UI와 플레이어 객체가 만들어졌으니 안드로이드 액티비티 상에서 UI와 객체가 연동이 되게 만들어봅시다. 관련 코드는 다음과 같습니다.

    pcv.setPlayer(player);

     

    이 코드는 반드시 액티비티 생명주기 중 onCreate 부문에서 액티비티의 뷰를 가져온 이후에 해야 합니다. 

     

    팁1. ExoPlayer의 스레딩(Threading) 구조를 알아보자
    ExoPlayer의 개발자노트를 살펴보면 ExoPlayer는 어플리케이션의 Main Thread를 이용하는 것을 권장하고 있다.
    이는 Android의 Looper 개념과 관련이 있다. Looper는 반복적으로 들어오는 메시지를 처리하여 스레드에 전달합니다(정확히는 Handler). 한 스레드에 한 Looper만 가지는 것을 원칙으로 한다.. 
    ExoPlayer 인스턴스를 접근하는 스레드는 플레이어를 생성하면서 Looper를 만들어 명확히 특정지어지게 된다.  Looper가 불명확하면, 플레이어를 생성한 스레드의 Looper가 사용되어지고 있는 문제가 생기거나, 스레드가 Looper를 가지고 있지 않다면, 어플리케이션의 메인 스레드가 사용되어지는 문제가 발생한다. 어떠한 경우에도 플레이어가 접근하는 스레드의 Looper는 Player.getApplicationLooper 을 통해 만들어져야 한다.

     

    플레이리스트 넣기

    ExoPlayer는 플레이어와 플레이어 리스트를 따로 관리합니다. ExoPlayer 개발노트에는 이렇게 설명합니다.

     

    Playlist API는 MediaItem.Builder로 만들어지는 MediaItem으로 운영이 된다. 플레이어에서는 미디어컨텐츠들이MediaSourceFactory(클래스)를 통해 재생이 가능한 MediaSource(클래스)로 변환이 된다. 따로 설정을 하지 않은 경우, 변환은 DefaultMediaSourceFactory(클래스)를 통해 수행이 된다. DefaultMediaSourceFactory는 미디어컨텐츠들의 특성에 맞게 미디어소스를 만들어주는 역할을 한다.

    [원문번역][참고]http://exoplyaer.dev

     

    간단히 안드로이드 프로젝트 내에 있는 raw 폴더에 원하는 음악을 넣어서 리스트에 추가해 봅시다.

     

    팁2. 안드로이드 프로젝트에 음악 넣기
    안드로이드 프로젝트에서 이미지, 동영상, 음악 등의 리소스 파일들은 raw 폴더에 넣어 관리를 합니다. 하지만 프로젝트 생성 시 따로 raw 폴더가 생성되어있지 않기 때문에 직접 만들어줘야 합니다. 이번 음악 플레이어에 음악을 넣어 재생을 하기 위해서는 raw폴더가 필요합니다. 다음의 이미지를 참고해서 raw 폴더를 만들어 보겠습니다.

     

    ExoPlayer는 미디어컨텐츠를 uri로 받아오기 때문에 Local에 있는 파일의 uri를 어떻게 가져올지 생각을 해봅시다.

    원래 기본 코드는 다음과 같습니다.

     

    // 기본 코드
    MediaItem mediaItem = MediaItem.fromUri(videoUri);

     

    안드로이드에서는 내부 프로젝트에 있는 파일의 Identifier를 구하여 uri로 바꿔주는 클래스가 있습니다. 코드는 다음과 같습니다. 

     

    // 안드로이드 앱 로컬에 있는 음악 파일을 리스트에 넣기.
    
    int musicID = this.getResources().getIdentifier({"파일이름"}, "raw", this.getPackageName());
    Uri musicUri = RawResourceDataSource.buildRawResourceUri(musicID);
    mediaitem = MediaItem.fromUri(musicUri);

     

    위 기본 코드를 아래 코드로 대체를 하면 이제 안드로이드 Raw 폴더에 있는 노래를 리스트로 사용할 수 있습니다.

    이제 마지막으로 MediaItem의 개체로 변환된 음악을 리스트에 추가해주면 됩니다. 코드는 다음과 같습니다.

     

    // 리스트 생성하기
    List<Object> mediaItems = new ArrayList<>();
    
    // Insert - 로컬 미디어 컨텐츠 불러오는 코드
    
    // 리스트에 미디어 컨텐츠 추가하기
    mediaItems.add(mediaitem);

     

    위에 있는 코드를 파일이름만 변경하여 계속 반복하면 mediaItems 에는 더 많은 노래의 리스트를 담아낼 수 있을 겁니다.

     

    플레이어 재생 및 UI

    플레이 리스트도 만들어졌으니 이제 플레이어에 넣어서 음악을 재생시켜봅시다. 코드는 다음과 같습니다.

     

    // 음악 하나를 넣을 때
    // player.setMediaItem(mediaItem);
    
    // 플레이 리스트를 넣을 때
    player.setMediaItems(mediaItems);
    
    player.prepare();
    player.setPlayWhenReady(true);

     

    player 인스턴스가 setMediaItems 메소드에 mediaItems를 넣어 실행하게되면 위에서 만든 플레이리스트가 플레이어에 속하게 됩니다. 마치 CD플레이어에 CD를 얹어 놓은 것처럼요. prepare를 통해 재생 준비를 마치고, setPlayWhenReady 메소드를 통해 MainActivity가 켜지자마자 노래가 재생됩니다.

     

    이렇게 ExoPlayer를 이용하여 기본적인 음악 플레이어를 구성하실 수 있습니다.


    Sample. MainActivity.class

    package com.opensource.exoplayerhelloworld;
    
    import androidx.appcompat.app.AppCompatActivity;
    
    import android.net.Uri;
    import android.os.Bundle;
    import android.util.Log;
    
    import com.google.android.exoplayer2.MediaItem;
    import com.google.android.exoplayer2.SimpleExoPlayer;
    import com.google.android.exoplayer2.ui.PlayerControlView;
    import com.google.android.exoplayer2.upstream.RawResourceDataSource;
    
    import java.lang.reflect.Field;
    import java.util.ArrayList;
    import java.util.List;
    
    public class MainActivity extends AppCompatActivity {
    
        private SimpleExoPlayer player;
        private PlayerControlView pcv;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            pcv = findViewById(R.id.main_pcv);
    
            initializePlayer();
    
        }
    
        private void initializePlayer(){
            if(player == null){
                player = new SimpleExoPlayer.Builder(MainActivity.this).build();
                pcv.setPlayer(player);
    
                List<MediaItem> mediaItems = new ArrayList<>();
                makePlayList(mediaItems);
    
                player.setMediaItems(mediaItems);
                player.prepare();
                player.setPlayWhenReady(true);
            }
        }
    
        private void makePlayList(List<MediaItem> mediaitems){
            MediaItem mediaitem;
            Field[] raws = R.raw.class.getFields();
            for(int count=0; count < raws.length; count++){
                Log.e("Raw Asset", raws[count].getName());
                int musicID = this.getResources().getIdentifier(raws[count].getName(), "raw", this.getPackageName());
                Uri musicUri = RawResourceDataSource.buildRawResourceUri(musicID);
                mediaitem = MediaItem.fromUri(musicUri);
                mediaitems.add(mediaitem);
            }
        }
    }

     

    sample. activity_main.xml

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <com.google.android.exoplayer2.ui.PlayerControlView
            android:id="@+id/main_pcv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_constraintBottom_toBottomOf="parent" />
    
    </androidx.constraintlayout.widget.ConstraintLayout>

     

    예제영상

     

     

     

Designed by Tistory.