Giới thiệu
Bài viết này hướng dẫn cách phát video sử dụng TextureView kết hợp MediaPlayer trên Android. TextureView linh hoạt hơn SurfaceView (hỗ trợ animation, alpha, rotation, clipping) và là lựa chọn khuyên dùng cho phát video tùy chỉnh.
Các bước thực hiện
Bước 1: Tạo dự án mới
Mở Android Studio, chọn File → New Project và điền thông tin cần thiết.
Bước 2: Thiết kế giao diện (res/layout/activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="8dp"
tools:context=".MainActivity">
<TextureView
android:id="@+id/textureView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
Bước 3: Chuẩn bị video
Tạo thư mục assets/ (chuột phải module → New → Folder → Assets Folder) và copy file video (ví dụ: videoplayback.mp4) vào đó.
Bước 4: Viết code phát video (src/MainActivity.java)
import androidx.appcompat.app.AppCompatActivity;
import android.content.res.AssetFileDescriptor;
import android.graphics.SurfaceTexture;
import android.media.MediaPlayer;
import android.os.Build;
import android.os.Bundle;
import android.view.Surface;
import android.view.TextureView;
import java.io.IOException;
public class MainActivity extends AppCompatActivity
implements TextureView.SurfaceTextureListener, MediaPlayer.OnVideoSizeChangedListener {
private TextureView textureView;
private MediaPlayer mediaPlayer;
private AssetFileDescriptor fileDescriptor;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textureView = findViewById(R.id.textureView);
textureView.setSurfaceTextureListener(this);
mediaPlayer = new MediaPlayer();
try {
fileDescriptor = getAssets().openFd("videoplayback.mp4");
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
Surface surface = new Surface(surfaceTexture);
try {
mediaPlayer.setSurface(surface);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
// API 24+: dùng setDataSource(FileDescriptor) + prepareAsync
mediaPlayer.setDataSource(fileDescriptor);
mediaPlayer.prepareAsync();
mediaPlayer.setOnPreparedListener(mp -> mediaPlayer.start());
} else {
// API cũ
mediaPlayer.setDataSource(fileDescriptor.getFileDescriptor(),
fileDescriptor.getStartOffset(),
fileDescriptor.getLength());
mediaPlayer.prepareAsync();
mediaPlayer.setOnPreparedListener(mp -> mediaPlayer.start());
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
// Xử lý khi kích thước surface thay đổi (xoay màn hình, resize)
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
// Trả về true nếu muốn xóa surface, false để MediaPlayer quản lý
return false;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
// Được gọi mỗi khi frame mới sẵn sàng - có thể dùng để xử lý frame (filter, screenshot)
}
@Override
public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
// Video size đã biết, có thể điều chỉnh layout TextureView tại đây
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null;
}
if (fileDescriptor != null) {
try {
fileDescriptor.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Bước 5: Cấu hình Manifest (androidManifest.xml)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
package="app.com.sample">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
</intent-filter>
</activity>
</application>
</manifest>
Chạy ứng dụng
Kết nối thiết bị, nhấn Run. Video sẽ tự động phát khi TextureView sẵn sàng.
TextureView vs SurfaceView
| Đặc điểm | TextureView | SurfaceView |
|---|---|---|
| Hỗ trợ alpha/transparency | ✅ Có | ❌ Không (luôn ở trên cùng) |
| Hỗ trợ rotation/scale/animation | ✅ Có | ❌ Không |
| Hỗ trợ clipping (cắt góc bo tròn) | ✅ Có | ❌ Không |
| Hiệu năng | Hơi kém hơn (copy GPU→CPU→GPU) | Tốt nhất (direct GPU) |
| Độ trễ (latency) | Cao hơn một chút | Thấp nhất |
| API Level tối thiểu | 14 | 1 |
Thư viện hiện đại thay thế
Đối với ứng dụng thực tế, nên dùng:
- ExoPlayer (Media3): Thư viện chính thức của Google, hỗ trợ DASH, HLS, DRM, adaptive streaming, cache, v.v.
- VideoView: Đơn giản cho phát video cơ bản từ URL/file.
// Ví dụ ExoPlayer (Media3) đơn giản
implementation "androidx.media3:media3-exoplayer:1.4.0"
implementation "androidx.media3:media3-ui:1.4.0"
// Layout
<com.google.android.exoplayer2.ui.PlayerView
android:id="@+id/playerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
// Code
ExoPlayer player = new ExoPlayer.Builder(this).build();
playerView.setPlayer(player);
MediaItem mediaItem = MediaItem.fromUri("https://example.com/video.mp4");
player.setMediaItem(mediaItem);
player.prepare();
player.playWhenReady = true;
Lưu ý quan trọng
- Luôn gọi
release()trongonDestroy()để giải phóng codec phần cứng. - Xử lý lifecycle: pause/resume video trong
onPause()/onResume(). - Cần quyền
INTERNETnếu phát video từ network. - TextureView không hoạt động tốt với hardware surface trên một số thiết bị cũ - test kỹ.