You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android中使用TextureView编程实现多视频裁剪与播放问题求助

Android TextureView多视频播放与裁剪问题解决

一、先解决surfaceTexture must not be null异常

问题根源

你在onCreate里直接调用videoView1.getSurfaceTexture()时,TextureView的SurfaceTexture还没完成初始化,返回了null,传给new Surface()自然就触发参数异常了。TextureView的SurfaceTexture是在视图完成绘制后才会创建的,必须等它可用后再绑定MediaPlayer。

修复后的代码

给每个TextureView设置SurfaceTextureListener,在回调里处理MediaPlayer的初始化:

import androidx.appcompat.app.AppCompatActivity;

import android.media.MediaPlayer;
import android.os.Bundle;
import android.graphics.Matrix;
import android.view.Surface;
import android.view.TextureView;

import java.io.IOException;

public class MainActivity extends AppCompatActivity implements TextureView.SurfaceTextureListener {

    private TextureView videoView1;
    private TextureView videoView2;
    private MediaPlayer mediaPlayer1;
    private MediaPlayer mediaPlayer2;
    private final String videoPath = "android.resource://" + getPackageName() + "/" + R.raw.patila;

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

        videoView1 = findViewById(R.id.video_view_1);
        videoView2 = findViewById(R.id.video_view_2);
        
        // 设置SurfaceTexture监听
        videoView1.setSurfaceTextureListener(this);
        videoView2.setSurfaceTextureListener(this);
    }

    @Override
    public void onSurfaceTextureAvailable(TextureView surface, int width, int height) {
        // 根据TextureView的ID初始化对应MediaPlayer
        if (surface.getId() == R.id.video_view_1) {
            initMediaPlayer(mediaPlayer1, surface);
        } else if (surface.getId() == R.id.video_view_2) {
            initMediaPlayer(mediaPlayer2, surface);
        }
    }

    @Override
    public void onSurfaceTextureSizeChanged(TextureView surface, int width, int height) {
        // 视图尺寸变化时重新调整裁剪
        adjustVideoCrop(surface);
    }

    @Override
    public boolean onSurfaceTextureDestroyed(TextureView surface) {
        // 释放对应MediaPlayer资源
        if (surface.getId() == R.id.video_view_1) {
            releaseMediaPlayer(mediaPlayer1);
        } else if (surface.getId() == R.id.video_view_2) {
            releaseMediaPlayer(mediaPlayer2);
        }
        return true;
    }

    @Override
    public void onSurfaceTextureUpdated(TextureView surface) {
        // 视频帧更新回调,无需额外处理
    }

    // 初始化MediaPlayer的通用方法
    private void initMediaPlayer(MediaPlayer player, TextureView textureView) {
        if (player != null) {
            player.release();
        }
        player = new MediaPlayer();
        try {
            player.setDataSource(videoPath);
            player.setSurface(new Surface(textureView.getSurfaceTexture()));
            player.setOnPreparedListener(mp -> {
                mp.start();
                // 视频准备完成后设置裁剪
                adjustVideoCrop(textureView);
            });
            player.prepareAsync();
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 更新引用
        if (textureView.getId() == R.id.video_view_1) {
            mediaPlayer1 = player;
        } else {
            mediaPlayer2 = player;
        }
    }

    // 释放MediaPlayer资源
    private void releaseMediaPlayer(MediaPlayer player) {
        if (player != null) {
            player.stop();
            player.release();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        releaseMediaPlayer(mediaPlayer1);
        releaseMediaPlayer(mediaPlayer2);
    }
}

二、多视频裁剪实现

TextureView的裁剪通过设置变换矩阵实现,你可以自定义裁剪区域(比如只显示视频的左半部分、上半部分),或者调整显示比例。

示例:裁剪视频的上半部分

在adjustVideoCrop方法中添加矩阵变换逻辑:

private void adjustVideoCrop(TextureView textureView) {
    MediaPlayer player = textureView.getId() == R.id.video_view_1 ? mediaPlayer1 : mediaPlayer2;
    if (player == null || !player.isPlaying()) return;

    int videoWidth = player.getVideoWidth();
    int videoHeight = player.getVideoHeight();
    int viewWidth = textureView.getWidth();
    int viewHeight = textureView.getHeight();

    Matrix matrix = new Matrix();
    // 缩放高度为2倍,再向上平移半个视图高度,实现只显示上半部分
    float scaleY = 2f;
    matrix.setScale(1f, scaleY);
    matrix.postTranslate(0, -viewHeight / 2f);

    textureView.setTransform(matrix);
}

其他裁剪场景

  • 裁剪左半部分:设置scaleX=2f,postTranslate(-viewWidth/2f, 0)
  • 自定义矩形区域:通过组合缩放、平移矩阵实现任意区域裁剪
  • 比例适配裁剪:先计算视频与视图的比例,调整矩阵避免画面拉伸

注意事项

  • 每个MediaPlayer对应一个TextureView,避免资源冲突
  • 务必在onSurfaceTextureDestroyed和onDestroy中释放MediaPlayer,防止内存泄漏
  • 该裁剪是视图层的显示变换,不会修改原视频文件

内容的提问来源于stack exchange,提问作者ashwyll solutions

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 11:20:37