Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Android

Phát video trên Android TextureView

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() trong onDestroy() để giải phóng codec phần cứng.
  • Xử lý lifecycle: pause/resume video trong onPause()/onResume().
  • Cần quyền INTERNET nế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ỹ.