Mediaplayer와 VideoView 관계

2024. 3. 12. 00:08Android/개발 CASE

VideoView 

비디오뷰는 안드로이드 위젯의 하나로 MediaPlayer를 이용한 비디오 파일 관련 구현을 내부에 포함하고 있다. 따라서 단순히 레이아웃에 위젯을 추가하고 비디오 파일을 지정하는 것만으로 쉽게 비디오 파일을 재생할 수 있다. 물론 세부적인 컨트롤을 하려면 뒤에 소개하는 것처럼 MediaPlayer를 직접 사용해야 하지만, 대부분의 경우는 VideoView로 원하는 목적을 달성할 수 있다. 

가장 먼저 할 일은 레이아웃에 비디오뷰 위젯을 추가하는 것이다. 다음은 길이는 부모와 같고, 높이는 210dp가 되는 비디오뷰의 예제이다.

 

<VideoView

    android:id="@+id/screenVideoView"

    android:layout_width="match_parent"

    android:layout_height="210dp">

</VideoView>

 

다음으로 할 일은 비디오뷰에 재생하기 원하는 비디오 파일을 지정하는 것이다. 예를 들어 프로젝트의 raw 디렉토리에 포함된 sample.mp4를 재생하기 위해서는 다음과 같이 한다.

 

// 비디오뷰 가져오기

mVideoView = (VideoView) findViewById(R.id.screenVideoView);

// sample.mp4 설정

Uri uri = Uri.parse("android.resource://" + getPackageName() + "/raw/sample");

mVideoView.setVideoURI(uri);

 

마지막으로 준비가 완료되면 비디오를 재생해 주기 위해 리스너를 등록한다.

 

// 리스너 등록

mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {

    @Override

    public void onPrepared(MediaPlayer mp) {

        // 준비 완료되면 비디오 재생

        mp.start();

    }

});

 

이것으로 앱을 실행하면 sample.mp4 비디오가 플레이 되는 것을 볼 수 있다.

 

MediaPlayer 

비디오뷰는 알고보면 MediaPlayer관련 구현을 내부에 감춘 래퍼에 불과한데, 이를 사용하지 않고 MediaPlayer를 직접 사용해서 비디오를 재생하는 것도 간단히 할 수 있다. 굳이 비디오뷰를 쓰지 않고 MediaPlayer를 사용하는 것은 비디오뷰로는 할 수 없는 세부적인 조작이 필요할 때가 대부분이다. 

가장 먼저 할 일은 레이아웃에 MediaPlayer가 비디오를 출력할 타겟을 추가하는 것이다. 여러 MediaPlayer 예제들은 SurfaceView를 사용하는데, 이 경우 단순한 동작에는 큰 무리가 없지만 비디오 텍스쳐를 조작해서 다른 곳에 이용하거나 하는 등의 응용 동작이 어려워진다. 원래 SurfaceView의 목적은 유저가 인터렉션을 하며 그림을 그리는 등의 기능을 위한 것으로 스트리밍이나 비디오 파일의 재생은 TextureView를 사용하는게 더 적합하다. 

한 가지 주의할 점은 TextureView를 사용할 경우 반드시 하드웨어 가속이 지원되는 겨우에만 재생이 가능하다는 점이다. 하드웨어 가속지 지원되지 않는 비디오 포맷의 경우 화면에 아무 것도 나타나지 않으니 이런 경우에는 SurfaceView를 사용해야 한다. 사실 밑으로 더 내려가면 하드웨어 지원 코덱의 경우 이온 버퍼를 사용해 디코더->렌더 사이의 카피를 없앤 것이 원인인데, 고해상도 영상일수록 이 제로 카피가 성능에 큰 영향을 미친다. 여건이 허락하지 않는 상황을 제외한다면, 배터리 효율이나 성능 면에서 가능하면 하드웨어 코덱이 지원되는 영상 포맷을 사용하고 TextureView를 쓰는 것을 추천한다. 

이번 예제에서는 TextureView를 사용해 보려한다. 다음은 레이아웃에 추가된 높이 210dp의 TexureView 예제이다.

 

<TextureView

     android:id="@+id/screenTextureView"

     android:layout_width="match_parent"

     android:layout_height="210dp">

</TextureView>

 

다음으로 할 일은 만들어진 TextureView에 리스너를 추가하는 것이다. 일단 뷰가 준비된 이후 시점이 되어야 MediaPlayer를 준비할 수 있기 대문이다. TextureView.SurfaceTextureListener가 이 용도로 사용되는 리스너이다. 다음은 이를 구현한 MyTexureViewListener 예제이다.

 

protected class MyTexureViewListener implements TextureView.SurfaceTextureListener {

        Context mContext;

        String mVideoSource;

        public MyTexureViewListener(Context context, String source) {

            // 생성자는 편의상 컨텍스트와 비디오 파일의 이름을 받아서 저장해 둔다.

            mContext = context;

            mVideoSource = source;

        }

        @Override

        // 텍스쳐가 준비되면 불리는 메서드이다. 여기에서 초기화를 처리한다.

        public void onSurfaceTextureAvailable(

            SurfaceTexture surfaceTexture, int width, int height) {

            try {

                // 먼저 사용할 미디어 플레이어를 만든다.

                mPlayer = new MediaPlayer();

                // 인자로 들어온 surfaceTexture를 기반으로 Surface를 하나 생성한다.

                Surface surface = new Surface(surfaceTexture);                

                // 만들어진 Surface를 미디어 플레이어에 세팅한다.

                mPlayer.setSurface(surface);

                // 미디어 플레이어의 비디오 소스를 세팅한다.

                // 이 경우에는 raw 디렉토리의 sample.mp4다. 참고로 .mp4는 생략하고 ~/sample로 접근한다.

                Uri uri = Uri.parse(

                    "android.resource://" + getPackageName() + "/raw/" + mVideoSource);

                mPlayer.setDataSource(mContext, uri);

                // 이제 미디어 플레이어를 준비시킨다.

                // 준비 함수는 prepare()와 prepareAsync()가 있는데 각각 동기, 비동기 버전이다.

                // 지금과 같은 로컬 파일의 경우 prepare()로 동기 처리를 해도 되지만, 

                // 스트리밍을 할 때에는 반드시 prepareAsync()로 비동기 처리를 해야 한다.

                // 모든 경우에 그냥 비동기 처리를 하는 것이 좋은 습관이다.

                mPlayer.prepareAsync();

                // 준비가 끝나는 지점을 알기 위해 리스너를 등록한다.

                // 비디오뷰에서 썼던 것과 같은 리스너이다.

                mPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {

                    @Override

                    public void onPrepared(MediaPlayer mp) {

                        // 준비가 완료되면 비디오를 재생한다.

                        mp.start();

                    }

                });

            }

            catch (Exception e) {

                e.printStackTrace();

            }

        }

        @Override

        public void onSurfaceTextureSizeChanged(

            SurfaceTexture surfaceTexture, int width, int height) {

            // 텍스쳐 사이즈가 변경되면 불린다. 여기에서는 처리하지 않는다.

        }

        @Override

        public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {

            // 텍스쳐가 파괴되면 불린다. 여기에서는 처리하지 않는다.

            return false;

        }

        @Override

        public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {

            // 텍스쳐 업데이트가 일어나면 불린다. 미디어 플레이어가 계속 업데이트 중이므로 여기에서는 처리하지 않는다.

        }

}

 

MyTexureViewListener는 onCreate 등 시점에서 다음과 같이 설정해 준다.

 

// 현재 액티비티를 컨텍스트로 주고, sample.mp4를 재생한다.

// Raw 디렉토리 밑의 파일을 URI로 접근할 때는 확장자를 생략하므로 sample만 전달한다.

mTextureView.setSurfaceTextureListener(new MyTexureViewListener(this, "sample"));

 

이제 앱을 실행하면 sample.mp4가 재생되는 것을 확인할 수 있다. 

 

참고 url : https://calvinjmkim.tistory.com/m/51?category=813064