Android 音视频开发 - VideoView

1: VideoView简介

VideoView是一个用于播放视频的视图组件,可以方便地在应用程序中播放本地或网络上的视频文件。

VideoView可以直接在布局文件中使用,也可以在代码中动态创建。

它封装了MediaPlayer和SurfaceView,提供了简单的接口来控制视频的播放和显示。

它提供了一系列方法来控制视频的播放、暂停、停止等操作,并且支持全屏播放和视频控制器的显示。

VideoView播放视频非常简单,只需要指定视频的URL或本地路径.

2: 使用

以下是VideoView的简单使用:

2.1 布局

在XML布局文件中添加VideoView组件.

ini 复制代码
<VideoView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    android:id="@+id/videoview"
    />

2.2 设置视频源

代码如下:

ini 复制代码
videoView = findViewById(R.id.videoview);
videoView.setVideoPath("sdcard/test.mp4");

除了setVideoPath外,我们还可以调用:

  1. setVideoURI(Uri uri)
  2. setVideoURI(Uri uri, Map<String, String> headers)

当然不管是setVideoPath或者setVideoURI实际都是执行的setVideoURI(Uri uri, Map<String, String> headers).

源码如下:

scss 复制代码
/**
 * Sets video path.
 *
 * @param path the path of the video.
 */
public void setVideoPath(String path) {
    setVideoURI(Uri.parse(path));
}

/**
 * Sets video URI.
 *
 * @param uri the URI of the video.
 */
public void setVideoURI(Uri uri) {
    setVideoURI(uri, null);
}

2.3 播放视频

ini 复制代码
videoView.start();

我们可以看下start()的源码:

ini 复制代码
@Override
public void start() {
    if (isInPlaybackState()) {
        mMediaPlayer.start();
        mCurrentState = STATE_PLAYING;
    }
    mTargetState = STATE_PLAYING;
}

可以看到实际上调用mMediaPlayer.start();另外设置了当前的状态为STATE_PLAYING.

这里直接调用了mMediaPlayer.start();那mMediaPlayer是什么时机初始化的呢?

查看源码可以看到:

ini 复制代码
private void openVideo() {
    if (mUri == null || mSurfaceHolder == null) {
        // not ready for playback just yet, will try again later
        return;
    }
    // we shouldn't clear the target state, because somebody might have
    // called start() previously
    release(false);

    if (mAudioFocusType != AudioManager.AUDIOFOCUS_NONE) {
        // TODO this should have a focus listener
        mAudioManager.requestAudioFocus(null, mAudioAttributes, mAudioFocusType, 0 /*flags*/);
    }

    try {
        mMediaPlayer = new MediaPlayer();
        // TODO: create SubtitleController in MediaPlayer, but we need
        // a context for the subtitle renderers
        final Context context = getContext();
        final SubtitleController controller = new SubtitleController(
                context, mMediaPlayer.getMediaTimeProvider(), mMediaPlayer);
        controller.registerRenderer(new WebVttRenderer(context));
        controller.registerRenderer(new TtmlRenderer(context));
        controller.registerRenderer(new Cea708CaptionRenderer(context));
        controller.registerRenderer(new ClosedCaptionRenderer(context));
        mMediaPlayer.setSubtitleAnchor(controller, this);

        if (mAudioSession != 0) {
            mMediaPlayer.setAudioSessionId(mAudioSession);
        } else {
            mAudioSession = mMediaPlayer.getAudioSessionId();
        }
        mMediaPlayer.setOnPreparedListener(mPreparedListener);
        mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
        mMediaPlayer.setOnCompletionListener(mCompletionListener);
        mMediaPlayer.setOnErrorListener(mErrorListener);
        mMediaPlayer.setOnInfoListener(mInfoListener);
        mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
        mCurrentBufferPercentage = 0;
        mMediaPlayer.setDataSource(mContext, mUri, mHeaders);
        mMediaPlayer.setDisplay(mSurfaceHolder);
        mMediaPlayer.setAudioAttributes(mAudioAttributes);
        mMediaPlayer.setScreenOnWhilePlaying(true);
        mMediaPlayer.prepareAsync();

        for (Pair<InputStream, MediaFormat> pending: mPendingSubtitleTracks) {
            try {
                mMediaPlayer.addSubtitleSource(pending.first, pending.second);
            } catch (IllegalStateException e) {
                mInfoListener.onInfo(
                        mMediaPlayer, MediaPlayer.MEDIA_INFO_UNSUPPORTED_SUBTITLE, 0);
            }
        }

        // we don't set the target state here either, but preserve the
        // target state that was there before.
        mCurrentState = STATE_PREPARING;
        attachMediaController();
    } catch (IOException ex) {
        Log.w(TAG, "Unable to open content: " + mUri, ex);
        mCurrentState = STATE_ERROR;
        mTargetState = STATE_ERROR;
        mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);
        return;
    } catch (IllegalArgumentException ex) {
        Log.w(TAG, "Unable to open content: " + mUri, ex);
        mCurrentState = STATE_ERROR;
        mTargetState = STATE_ERROR;
        mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);
        return;
    } finally {
        mPendingSubtitleTracks.clear();
    }
}

可以看到openVideo()

  1. release()方法释放正在播放的视频.
  2. 初始化mMediaPlayer,传入Uri,设置状态 STATE_PREPARING。
  3. attachMediaController()绑定MediaPlayer与VideoView。

最后openVideo()则是在setVideoURI(Uri uri, Map<String, String> headers)内调用。

这样其实已经可以播放指定的视频了。

下面的方法可选。

2.4 MediaController控制器

MediaController是一个用于控制媒体播放器的视图组件。

MediaController的使用步骤如下:

  1. 创建一个MediaController对象:MediaController mediaController = new MediaController(context);
  2. 将MediaController与媒体播放器组件关联:mediaController.setMediaPlayer(mediaPlayer);
  3. 将MediaController添加到布局中:layout.addView(mediaController);
arduino 复制代码
videoView.setMediaController(new MediaController(this));
videoView.start().

直接调用setMediaController,运行后我们可以看到与之前直接调用start()的区别就是多了个控制器的显示。其中包含一组常用的媒体控制按钮,如播放/暂停、快进/快退、前进/后退等,并且可以与MediaPlayer或VideoView等媒体播放器组件进行关联.

我们可以看下源码:

scss 复制代码
public void setMediaController(MediaController controller) {
    if (mMediaController != null) {
        mMediaController.hide();
    }
    mMediaController = controller;
    attachMediaController();
}

可以看到做的操作如下:

  1. 如果存在mMediaController,则调用hide方法。
  2. 对mMediaController赋值
  3. attachMediaController
相关推荐
字节跳动视频云技术团队1 天前
火山引擎 AI MediaKit X 懂车帝,探索汽车内容智能创作新方式
人工智能·音视频开发
老孙讲技术4 天前
【监控开发】把车间未戴帽和烟火报警接进安监值班群:workwearDetect 对图与 setMessageCallback 订 smokeAlarm
后端·物联网·音视频开发
字节跳动视频云技术团队7 天前
豆包视频通话升级,火山引擎多模态传输系统提供技术支撑
人工智能·音视频开发
搬砖记录员7 天前
录屏总黑屏?90% 的人没关这个开关——浏览器硬件加速冲突排查指南
前端·音视频开发
老孙讲技术9 天前
把工地遮挡和掉线接进项目部:setMessageCallback 订 alarm 与 deviceStatus
后端·物联网·音视频开发
老孙讲技术9 天前
关店后有人进门,监控值班群却没响:用 setMessageCallback 订 human,再用 aiHuman 打开人形
后端·物联网·音视频开发
老孙讲技术9 天前
把园区几百路摄像头收进值班台账:bindDevice 入账与 listDeviceDetailsByPage 翻页
后端·物联网·音视频开发
老孙讲技术9 天前
把没网没电的户外点位接进出画值班台:unBindDeviceInfo 核 SIMCard 与 bindDeviceLive
后端·物联网·音视频开发
字节跳动视频云技术团队10 天前
从出片到出海:AI MediaKit 重塑短剧全链路生产力
人工智能·音视频开发
小小杨树13 天前
【C++】学习:双缓冲视频流水线深解析
c++·性能优化·音视频开发