Android에서 비디오 재생 구현하기

VideoView를 사용한 동영상 재생

Android 앱에서 동영상을 재생하는 대표적인 방법 중 하나는 VideoView 위젯을 활용하는 것입니다. 이 클래스는 SurfaceView를 상속하며, 간단한 API만으로 로컬 또는 네트워크상의 미디어 파일을 재생할 수 있도록 도와줍니다. 패키지 경로는 android.widget.VideoView이며, 개발자가 직접 Surface 관리나 코덱 처리를 할 필요 없이 손쉽게 동영상 플레이 기능을 구현할 수 있습니다.

주요 메서드 소개

  • setVideoPath(String path): 로컬 경로에 있는 동영상 파일을 설정합니다.
  • setVideoURI(Uri uri): URI 기반으로 동영상 소스를 지정합니다 (예: 인터넷 스트리밍).
  • start(): 재생을 시작합니다.
  • pause(): 일시 정지합니다.
  • resume(): 일시 정지 후 다시 재생합니다.
  • stopPlayback(): 재생을 완전히 중단하고 리소스를 해제합니다.
  • seekTo(int msec): 특정 시간(ms 단위)으로 이동하여 재생합니다.
  • getDuration(): 전체 재생 길이를 밀리초 단위로 반환합니다.
  • getCurrentPosition(): 현재 재생 위치를 반환합니다.
  • isPlaying(): 현재 재생 중인지 여부를 확인합니다.
  • setMediaController(MediaController): 제어 바를 연결하여 사용자 인터페이스를 강화합니다.
  • setOnPreparedListener(): 동영상 준비 완료 시 콜백을 받습니다.
  • setOnCompletionListener(): 재생 종료 시 이벤트를 감지합니다.
  • setOnErrorListener(): 오류 발생 시 처리할 수 있습니다.

VideoView는 내부적으로 MediaPlayer를 관리하므로 개별 초기화나 리소스 해제 코드가 필요 없습니다. start() 호출 시 자동으로 미디어를 로드하고 재생을 시작합니다.

기본 재생 예제

다음은 SD 카드에 저장된 동영상을 재생하고, 재생 상태를 제어하는 간단한 예제입니다.

레이아웃 파일: activity_video_player.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <EditText
        android:id="@+id/edit_video_path"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="/sdcard/sample.mp4" />

    <SeekBar
        android:id="@+id/seekbar_progress"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <Button
            android:id="@+id/btn_start"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="시작" />

        <Button
            android:id="@+id/btn_pause"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="일시정지" />

        <Button
            android:id="@+id/btn_restart"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="다시재생" />

        <Button
            android:id="@+id/btn_stop"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="정지" />
    </LinearLayout>

    <VideoView
        android:id="@+id/videoview_player"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />
</LinearLayout>

액티비티 코드: VideoPlayerActivity.java

package com.example.videoplayer;

import android.app.Activity;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Handler;
import android.widget.*;

import java.io.File;

public class VideoPlayerActivity extends Activity {
    private EditText editVideoPath;
    private SeekBar seekBarProgress;
    private Button btnStart, btnPause, btnRestart, btnStop;
    private VideoView videoViewPlayer;

    private boolean isPlaying = false;
    private final Handler handler = new Handler();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_video_player);

        initViews();
        setupClickListeners();
        setupSeekBar();
    }

    private void initViews() {
        editVideoPath = findViewById(R.id.edit_video_path);
        seekBarProgress = findViewById(R.id.seekbar_progress);
        btnStart = findViewById(R.id.btn_start);
        btnPause = findViewById(R.id.btn_pause);
        btnRestart = findViewById(R.id.btn_restart);
        btnStop = findViewById(R.id.btn_stop);
        videoViewPlayer = findViewById(R.id.videoview_player);
    }

    private void setupClickListeners() {
        btnStart.setOnClickListener(v -> playFrom(0));
        btnPause.setOnClickListener(v -> togglePause());
        btnRestart.setOnClickListener(v -> replay());
        btnStop.setOnClickListener(v -> stopPlayback());
    }

    private void setupSeekBar() {
        seekBarProgress.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                if (fromUser && videoViewPlayer != null) {
                    videoViewPlayer.seekTo(progress);
                }
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {}

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {}
        });
    }

    private void playFrom(int startPosition) {
        String path = editVideoPath.getText().toString().trim();
        File file = new File(path);

        if (!file.exists()) {
            Toast.makeText(this, "파일을 찾을 수 없습니다.", Toast.LENGTH_SHORT).show();
            return;
        }

        videoViewPlayer.setVideoPath(file.getAbsolutePath());
        videoViewPlayer.start();
        videoViewPlayer.seekTo(startPosition);

        seekBarProgress.setMax(videoViewPlayer.getDuration());
        startProgressUpdater();
        btnStart.setEnabled(false);
        isPlaying = true;

        videoViewPlayer.setOnCompletionListener(mp -> {
            btnStart.setEnabled(true);
            isPlaying = false;
        });

        videoViewPlayer.setOnErrorListener((mp, what, extra) -> {
            Toast.makeText(this, "재생 오류", Toast.LENGTH_SHORT).show();
            isPlaying = false;
            return false;
        });
    }

    private void startProgressUpdater() {
        Runnable updateRunnable = new Runnable() {
            @Override
            public void run() {
                if (isPlaying && videoViewPlayer != null && videoViewPlayer.isPlaying()) {
                    seekBarProgress.setProgress(videoViewPlayer.getCurrentPosition());
                    handler.postDelayed(this, 500);
                }
            }
        };
        handler.post(updateRunnable);
    }

    private void togglePause() {
        if (videoViewPlayer.isPlaying()) {
            videoViewPlayer.pause();
            btnPause.setText("계속");
        } else {
            videoViewPlayer.start();
            btnPause.setText("일시정지");
        }
    }

    private void replay() {
        if (videoViewPlayer != null) {
            videoViewPlayer.seekTo(0);
            videoViewPlayer.start();
        }
    }

    private void stopPlayback() {
        if (videoViewPlayer != null && videoViewPlayer.isPlaying()) {
            videoViewPlayer.stopPlayback();
            btnStart.setEnabled(true);
            isPlaying = false;
        }
    }
}

MediaController와 연동하기

MediaControllerVideoView와 함께 사용되어 사용자 친화적인 제어 UI를 제공하는 클래스입니다. 기본 재생, 일시정지, 탐색, 진행률 표시 등을 포함한 풍부한 컨트롤 바를 화면에 띄울 수 있으며, 터치 입력 후 일정 시간이 지나면 자동으로 사라지는 오버레이 형태로 동작합니다.

MediaController의 주요 기능

  • setMediaPlayer(MediaPlayerControl): 제어할 미디어 컴포넌트를 지정합니다. VideoView는 이 인터페이스를 구현합니다.
  • setPrevNextListeners(): 이전/다음 동영상 전환 버튼에 리스너를 설정합니다. 등록하지 않으면 버튼이 나타나지 않습니다.
  • isShowing(): 현재 컨트롤러가 화면에 표시되고 있는지 확인합니다.

MediaController 사용 예제

다음은 MediaController를 통해 UI 없이도 재생 제어를 가능하게 하는 예제입니다.

레이아웃: activity_media_controller.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <VideoView
        android:id="@+id/video_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</FrameLayout>

코드: MediaControllerActivity.java

package com.example.videoplayer;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.MediaController;
import android.widget.Toast;
import android.widget.VideoView;

import java.io.File;

public class MediaControllerActivity extends Activity {
    private VideoView videoView;
    private MediaController mediaController;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_media_controller);

        videoView = findViewById(R.id.video_view);
        mediaController = new MediaController(this);

        File videoFile = new File("/sdcard/sample.mp4");
        if (videoFile.exists()) {
            videoView.setVideoPath(videoFile.getAbsolutePath());

            // 양방향 연결 필수
            videoView.setMediaController(mediaController);
            mediaController.setMediaPlayer(videoView);

            // 이전/다음 버튼 리스너 추가 (옵션)
            mediaController.setPrevNextListeners(
                v -> Toast.makeText(this, "다음 영상", Toast.LENGTH_SHORT).show(),
                v -> Toast.makeText(this, "이전 영상", Toast.LENGTH_SHORT).show()
            );
        } else {
            Toast.makeText(this, "동영상 파일이 없습니다.", Toast.LENGTH_LONG).show();
        }
    }
}

결론

VideoView는 간단한 동영상 재생 기능을 빠르게 구현할 수 있는 강력한 도구입니다. 특히 MediaController와 조합하면 별도의 UI 개발 없이도 전문적인 플레이어 인터페이스를 제공할 수 있습니다. 하지만 고급 기능(자막 지원, 다양한 포맷 디코딩, 맞춤형 UI 등)이 필요한 경우, 더 많은 제어 권한을 제공하는 SurfaceView + MediaPlayer 조합이나 ExoPlayer 같은 외부 라이브러리 사용을 고려해야 합니다.

태그: VideoView MediaController Android Video Playback MediaPlayer SeekBar

6월 5일 19:04에 게시됨