728x90
반응형
MediaPlayer
- 로컬 또는 원격(인터넷) 미디어 파일 재생 가능
- 오디오 및 비디오 재생 가능
- 상태 변화(State) 관리 필요
- SurfaceVIew, TextureView, VideoView를 사용하여 비디오 렌더링 가능
- 재생, 일시 정지, 정지, 시크(Seek) 등의 기능 제공
MediaPlayer 상태 흐름
Idle → Initialized → Preparing → Prepared → Started → Paused → Stopped → Released
상태 | 설명 |
Idle | MediaPlayer 객체가 생성된 초기 상태 |
Initialized | setDataSource()로 재생할 파일 설정 후 상태 |
Preparing | prepareAsync() 호출 후 미디어 준비 중 |
Prepared | 미디어 준비 완료 (onPrepared() 호출됨) |
Strated | start() 호출 후 재생 상태 |
Paused | pause() 호출 후 재생이 일시 중지됨 |
Stopped | stop() 호출 후 다시 prepare() 필요 |
Released | release() 호출 후 MediaPlayer 객체 해제 |
SurfaceView
안드로이드에서 화면에 그래픽(Rendering) 또는 미디어(Media) 데이터를 효율적으로 표시하기 위한 뷰(View)
동영상 재생(MediaPlayer), 게임 그래픽(Rendering), 카메라 미리 보기 등과 같이 빠른 렌더링이 필요한 경우 사용
특징
- 별도의 Surface를 제공 → 일반적인 View와 다르게 별도의 Surface에서 동작하여 렌더링 성능 향상
- UI Thread가 아닌 별도 Thread에서 렌더링 가능 → Canvas를 사용하여 직접 그리기 가능
- MediaPlayer, OpenGL, Camera API와 함께 사용 가능
- 빠른 화면 갱신이 필요할 때 적합 → 비디오 재생, 실시간 스트리밍, 게임 등에서 사용
SurfaceView vs TextureView
비교 항목 | SurfaceView | TextureView |
렌더링 방식 | 별도의 Surface에서 렌더링 | 일반 뷰 계층에서 렌더링 |
성능 | 빠름 (전용 Surface 사용) | 다소 느림 (뷰 계층 내에서 처리) |
업데이트 방식 | SurfaceRenderer를 사용하여 직접 렌더링 | Canvas를 사용하여 뷰 내부에서 렌더링 |
투명도 지원 | 지원하지 않음 | 지원 |
뷰 애니메이션 | 직접 구현 필요 | 뷰 애니메이션 가능 |
주요 사용처 | 비디오 재생, 게임, 카메라 미리보기 | 부드러운 UI 애니메이션, OpenGL 렌더링 |
SurfaceView 기본 사용법
SurfaceView를 사용하려면 SurfaceHolder.Callback을 구현해야 한다.
SurfaceHolder.Callback는 SurfaceView가 생성되거나 변경될 때 호출되는 이벤트를 처리한다.
예제:SurfaceView로 화면 그리기
class CustomSurfaceView(context: Context, attrs: AttributeSet) : SurfaceView(context, attrs), SurfaceHolder.Callback {
private val drawThread = DrawThread(holder)
init {
holder.addCallback(this) // SurfaceHolder 콜백 추가
}
override fun surfaceCreated(holder: SurfaceHolder) {
drawThread.startDrawing()
}
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {}
override fun surfaceDestroyed(holder: SurfaceHolder) {
drawThread.stopDrawing()
}
private class DrawThread(private val holder: SurfaceHolder) : Thread() {
private var running = false
fun startDrawing() {
running = true
start()
}
fun stopDrawing() {
running = false
join()
}
override fun run() {
while (running) {
val canvas = holder.lockCanvas()
if (canvas != null) {
try {
canvas.drawColor(Color.BLACK) // 배경색 설정
val paint = Paint().apply { color = Color.RED }
canvas.drawCircle(200f, 200f, 100f, paint) // 원 그리기
} finally {
holder.unlockCanvasAndPost(canvas) // 화면 업데이트
}
}
}
}
}
}
- SurfaceHolder.Callback을 구현하여 Surface의 상태 변화 감지
- 별도의 스레드(DrawThread)에서 Canvas를 가져와 그림을 직접 그릴 수 있음
- lockCanvas() → 그리기 작업 수행 → unlockCanvasAndPost() 호출하여 화면 갱신
SurfaceView를 활용한 MediaPlayer 동영상 재생
권한 추가
<uses-permission android:name="android.permission.INTERNET"/>
- 원격 URL에서 동영상을 재생할 경우 필요
코드
class VideoPlayerActivity : AppCompatActivity(), SurfaceHolder.Callback {
private lateinit var mediaPlayer: MediaPlayer
private lateinit var surfaceView: SurfaceView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_video_player)
surfaceView = findViewById(R.id.surfaceView)
val holder = surfaceView.holder
holder.addCallback(this)
}
override fun surfaceCreated(holder: SurfaceHolder) {
mediaPlayer = MediaPlayer()
try {
mediaPlayer.setDataSource("https://www.example.com/sample_video.mp4") // URL 또는 로컬 파일
mediaPlayer.setDisplay(holder)
mediaPlayer.prepareAsync()
mediaPlayer.setOnPreparedListener {
mediaPlayer.start() // 준비 완료 후 자동 재생
}
mediaPlayer.setOnCompletionListener {
Toast.makeText(this, "재생 완료", Toast.LENGTH_SHORT).show()
}
} catch (e: IOException) {
e.printStackTrace()
}
}
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {}
override fun surfaceDestroyed(holder: SurfaceHolder) {
mediaPlayer.release()
}
override fun onDestroy() {
super.onDestroy()
if (::mediaPlayer.isInitialized) {
mediaPlayer.release()
}
}
}
- SurfaceView를 사용하여 MediaPlayer가 영상을 렌더링
- prepareAsync()로 비동기 방식으로 미디어 준비 후 onPrepared()에서 start() 호출
- onCompletionListener에서 재생 완료 감지
추가 기능
재생 컨트롤 추가 (Play, Pause, Seek)
buttonPlay.setOnClickListener {
if (!mediaPlayer.isPlaying) mediaPlayer.start()
}
buttonPause.setOnClickListener {
if (mediaPlayer.isPlaying) mediaPlayer.pause()
}
buttonSeek.setOnClickListener {
mediaPlayer.seekTo(30000) // 30초로 이동
}
- start(), pause(), seekTo() 등을 활용하여 컨트롤 구현 가능
재생 시간 표시 (현재 시간, 총 길이)
val duration = mediaPlayer.duration / 1000 // 초 단위
val currentPosition = mediaPlayer.currentPosition / 1000
textView.text = "재생 시간: $currentPosition / $duration 초"
- duration으로 총길이, currentPosition으로 현재 위치 조회 가능
비디오 재생 방법 비교 : MediaPlayer vs VideoView vs ExoPlayer
방식 | 특징 | 장점 | 단점 | 권장 사용처 |
MediaPlayer | 저수준 API, SurfaceView 또는 TextureView 필요 | 세부 제어 가능, 다양한 포맷 지원 | 직접 UI 및 컨트롤러 구현 필요, 버퍼링 최적화 부족 | 로컬 파일 재생, 커스텀 플레이어 필요할 때 |
VideoView | MediaPlayer를 내부적으로 사용 | 사용법 간단, 기본 컨트롤러 제공 (MediaController) | 기능 제한(자막, DRM 지원 부족), 버퍼링 최적화 부족 | 간단한 로컬/온라인 동영상 재생 |
ExoPlayer | Google에서 개발한 고급 미디어 플레이어 | 스트리밍 최적화, DASH/HLS 지원, 자막/DRM 지원 | 복잡한 API, 파일 크기 증가 (라이브러리 포함 필요) | 네트워크 스트리밍, 고급 기능 필요할 때 |
- 간단한 비디오 재생이 필요: VideoView
- 로컬 비디오 + UI 커스텀 필요: MediaPlayer
- 스트리밍 최적화, 자막/DRM 필요: ExoPlayer
728x90
반응형
'안드로이드 학습(Kotlin)' 카테고리의 다른 글
22. Foreground Service를 활용한 음악 재생 (1) | 2025.02.10 |
---|---|
21. WorkManager 활용 예시 (0) | 2025.02.07 |
19. 브로드캐스트 사용 (알람 등록하기, 메시지 전달하기) (1) | 2025.02.07 |
18. Android의 주요 데이터 저장소 방식 (2) | 2025.02.07 |
Geocoder란? (2) | 2025.02.06 |