首页 > 其他分享 >Android视频开发入门: VideoView、MediaPlayer、 FFmpeg、exoplayer...

Android视频开发入门: VideoView、MediaPlayer、 FFmpeg、exoplayer...

时间:2024-06-06 15:58:54浏览次数:16  
标签:... FFmpeg 视频 MediaPlayer 直播 exoplayer MediaCodec new 播放

现在,视频功能是越来越普遍的需求。本文将提供一个关于Android视频开发的入门指南,帮助读者快速掌握视频播放、录制和处理等基本功能。

1、概述

在Android平台上,视频开发主要涉及以下几个方面:
视频播放与控制
视频录制与处理
视频编解码与格式转换
视频流媒体与直播
接下来,我们将逐一介绍这些方面的基本概念和实现方法。

2、视频播放与控制

Android提供了多种视频播放的方法。其中,最常用的是VideoView和MediaPlayer。

2.1 VideoView

VideoView是一个封装了MediaPlayer的视图控件,它可以方便地在布局中添加,并通过简单的方法实现视频播放功能。
首先,在布局文件中添加VideoView:

<VideoView
    android:id="@+id/video_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"/>

然后,在Activity或Fragment中设置视频源并开始播放:

VideoView videoView = findViewById(R.id.video_view);
videoView.setVideoURI(Uri.parse("视频文件路径"));
videoView.start();

2.2 MediaPlayer

MediaPlayer是一个更底层的音视频播放类。相较于VideoView,它提供了更多的控制方法,但实现起来稍微复杂一些。
首先,创建一个MediaPlayer实例并设置监听器:

MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
    @Override
    public void onPrepared(MediaPlayer mp) {
        mp.start();
    }
});

接着,设置视频源并准备播放:

mediaPlayer.setDataSource("视频文件路径");
mediaPlayer.prepareAsync();

3、视频录制与处理

Android提供了MediaRecorder类来实现视频录制功能。首先,需要在AndroidManifest.xml中添加相应的权限:

<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

接下来,创建一个MediaRecorder实例并进行初始化:

MediaRecorder mediaRecorder = new MediaRecorder();
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP);
mediaRecorder.setOutputFile("视频输出文件路径");
mediaRecorder.prepare();

最后,开始录制:

mediaRecorder.start();

在录制完成后,需要调用stop()方法停止录制,并释放资源:

mediaRecorder.stop();
mediaRecorder.release();

4、视频编解码与格式转换

4.1 MediaCodec

Android提供了MediaCodec类来实现视频编解码和格式转换。在Android中,使用MediaCodec类进行视频编解码和格式转换是一种底层的方法。虽然使用起来较为复杂,但它可以为开发者提供更多的控制和定制能力。下面我们将结合代码示例来阐述如何使用MediaCodec实现视频编解码和格式转换。
以下代码示例展示了如何使用MediaCodec解码视频:
首先,创建一个MediaExtractor实例,用于从视频文件中提取数据:

MediaExtractor mediaExtractor = new MediaExtractor();
mediaExtractor.setDataSource("视频文件路径");
遍历视频文件的轨道,找到视频轨道,并设置MediaExtractor的轨道索引:

int videoTrackIndex = -1;
for (int i = 0; i < mediaExtractor.getTrackCount(); i++) {
    MediaFormat format = mediaExtractor.getTrackFormat(i);
    String mimeType = format.getString(MediaFormat.KEY_MIME);
    if (mimeType.startsWith("video/")) {
        videoTrackIndex = i;
        break;
    }
}
mediaExtractor.selectTrack(videoTrackIndex);

根据视频轨道的格式,创建一个MediaCodec实例来进行解码:

MediaFormat videoFormat = mediaExtractor.getTrackFormat(videoTrackIndex);
String videoMimeType = videoFormat.getString(MediaFormat.KEY_MIME);
MediaCodec videoDecoder = MediaCodec.createDecoderByType(videoMimeType);
videoDecoder.configure(videoFormat, null, null, 0);
videoDecoder.start();

使用MediaCodec进行解码:

boolean isDone = false;
while (!isDone) {
    // 从MediaCodec获取一个空的输入缓冲区,用于存放待解码的数据
    int inputBufferIndex = videoDecoder.dequeueInputBuffer(10000);
    if (inputBufferIndex >= 0) {
        // 获取到输入缓冲区
        ByteBuffer inputBuffer = videoDecoder.getInputBuffer(inputBufferIndex);
        // 从MediaExtractor读取一帧数据(一个sample)到输入缓冲区
        int sampleSize = mediaExtractor.readSampleData(inputBuffer, 0);
        if (sampleSize < 0) {
            // 所有数据都已读取完,将输入缓冲区标记为结束,并结束循环
            videoDecoder.queueInputBuffer(inputBufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
            isDone = true;
        } else {
            // 将输入缓冲区装有数据的部分送入MediaCodec进行解码
            videoDecoder.queueInputBuffer(inputBufferIndex, 0, sampleSize, mediaExtractor.getSampleTime(), 0);
            // 将MediaExtractor的读取位置向前推进一帧
            mediaExtractor.advance();
        }
    }

    // 创建一个BufferInfo实例,用于接收解码后的数据信息
    MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
    // 从MediaCodec获取一个装有解码后数据的输出缓冲区
    int outputBufferIndex = videoDecoder.dequeueOutputBuffer(bufferInfo, 10000);
    if (outputBufferIndex >= 0) {
        // 处理解码后的数据,例如将其渲染到Surface上
        videoDecoder.releaseOutputBuffer(outputBufferIndex, true);
    } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
        // 输出格式发生变化,可以在这里处理新的输出格式
    }
}

最后,释放资源:

videoDecoder.stop();
videoDecoder.release();
mediaExtractor.release();

这只是一个简单的示例,展示了如何使用MediaCodec解码视频。在实际开发中,可能需要处理更多的细节和错误情况。

4.2 FFmpeg

如果你觉得使用MediaCodec过于复杂,可以考虑使用第三方库,如FFmpeg,来实现视频编解码和格式转换功能。下面我们将结合代码示例来说明如何使用FFmpeg实现视频编解码和格式转换。
首先,需要将FFmpeg库导入到Android项目中。这里推荐使用mobile-ffmpeg库,它为Android提供了预编译的FFmpeg二进制文件。将库添加到build.gradle文件的依赖项中:

dependencies {
    implementation 'com.arthenica:mobile-ffmpeg-full:4.4.LTS'
}

接下来,我们将展示如何使用FFmpeg实现视频格式转换。假设我们需要将一个MP4格式的视频转换为MKV格式:

import com.arthenica.mobileffmpeg.Config;
import com.arthenica.mobileffmpeg.FFmpeg;

String inputVideoPath = "输入视频文件路径";
String outputVideoPath = "输出视频文件路径";
String[] ffmpegCommand = new String[]{"-i", inputVideoPath, "-c", "copy", outputVideoPath};

int result = FFmpeg.execute(ffmpegCommand);
if (result == Config.RETURN_CODE_SUCCESS) {
    Log.i("FFmpeg", "视频格式转换成功");
} else {
    Log.i("FFmpeg", "视频格式转换失败,错误码:" + result);
}

在这个示例中,我们使用FFmpeg.execute()方法执行FFmpeg命令。该命令将输入视频文件(MP4格式)转换为输出视频文件(MKV格式)。命令的参数包括输入文件路径、输出文件路径以及其他转换选项。

使用FFmpeg库可以简化视频编解码和格式转换的过程,同时提供了丰富的功能和选项。不过,需要注意的是,FFmpeg库的体积较大,可能会导致应用的安装包变大。在选择FFmpeg时,需要权衡功能和应用体积之间的关系。

5、视频流媒体与直播

实现视频流媒体和直播功能,通常需要借助第三方库和服务。常见的库有ExoPlayer、VLC等,而服务方面可以选择阿里云、腾讯云等提供的直播解决方案。
这里我们以ExoPlayer库和阿里云直播服务为例,来说明如何实现。
首先,我们需要在项目中引入ExoPlayer库,可以在build.gradle文件中添加如下依赖:

dependencies {
    implementation 'com.google.android.exoplayer:exoplayer:2.X.X'
}

然后,我们可以创建一个ExoPlayer实例来播放流媒体视频。以下是一个简单的例子:

// 创建一个默认的TrackSelector
BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
TrackSelection.Factory videoTrackSelectionFactory =
        new AdaptiveTrackSelection.Factory(bandwidthMeter);
TrackSelector trackSelector =
        new DefaultTrackSelector(videoTrackSelectionFactory);

// 创建ExoPlayer
SimpleExoPlayer player = ExoPlayerFactory.newSimpleInstance(context, trackSelector);

// 准备播放的媒体源
MediaSource mediaSource = new HlsMediaSource.Factory(
        new DefaultHttpDataSourceFactory("exoplayer-codelab")).
        createMediaSource(Uri.parse("http://path/to/streaming/media.m3u8"));

// 准备播放器
player.prepare(mediaSource);

// 开始播放
player.setPlayWhenReady(true);

以上代码创建了一个ExoPlayer实例,并准备了一个HLS媒体源进行播放。这里的媒体源URL是一个假的例子,实际使用时需要替换为真实的流媒体地址。

对于直播功能,我们通常需要借助第三方服务,如阿里云、腾讯云等。这些服务提供了直播推流和拉流的解决方案,我们只需要按照他们的SDK接入指南,将SDK集成到我们的应用中即可。
例如,如果我们选择阿里云的直播服务,首先需要在阿里云控制台创建一个直播流,并获取到推流地址和拉流地址。然后,在应用中集成阿里云的直播SDK,使用推流地址进行推流,使用拉流地址进行播放。
这只是一个简单的入门级介绍,实际的视频流媒体和直播开发可能会涉及到更多的技术细节和业务需求,如视频编码格式、网络条件适应、直播延迟优化、弹幕功能等。在开发过程中,需要不断学习和实践,以满足项目的需求。

6、进阶学习

6.1 自定义视频播放器

虽然Android提供了内置的视频播放组件,但在很多情况下,我们需要自定义视频播放器以满足特定的需求。在Android中实现自定义视频播放器,我们可以选择使用ExoPlayer库,它提供了丰富的API和灵活的自定义能力。以下是一个基本的示例:
1.首先,添加ExoPlayer的依赖项到build.gradle文件:

dependencies {
    implementation 'com.google.android.exoplayer:exoplayer:2.X.X'
}

2.在布局文件中,添加一个PlayerView控件作为视频播放的容器:

<com.google.android.exoplayer2.ui.PlayerView
    android:id="@+id/player_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"/>

3.在Activity或Fragment中,创建ExoPlayer实例,并将其绑定到PlayerView:

// 创建TrackSelector
TrackSelector trackSelector = new DefaultTrackSelector();

// 创建ExoPlayer实例
SimpleExoPlayer player = ExoPlayerFactory.newSimpleInstance(context, trackSelector);

// 绑定到PlayerView
PlayerView playerView = findViewById(R.id.player_view);
playerView.setPlayer(player);

4.创建媒体源并开始播放:

// 创建媒体源
MediaSource mediaSource = new ProgressiveMediaSource.Factory(new DefaultHttpDataSourceFactory("user-agent"))
        .createMediaSource(Uri.parse("视频文件URL"));

// 准备播放器
player.prepare(mediaSource);

// 开始播放
player.setPlayWhenReady(true);

以上代码创建了一个基本的视频播放器,可以播放指定URL的视频。但是,这只是最基础的功能。如果要实现自定义的播放控制(如播放/暂停按钮、进度条、全屏切换等),还需要更多的代码。

例如,如果要添加播放/暂停按钮,可以在布局文件中添加一个ImageButton,然后在其点击事件中切换播放状态:

ImageButton playPauseButton = findViewById(R.id.play_pause_button);
playPauseButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if (player.getPlayWhenReady()) {
            player.setPlayWhenReady(false);
            playPauseButton.setImageResource(R.drawable.ic_play);
        } else {
            player.setPlayWhenReady(true);
            playPauseButton.setImageResource(R.drawable.ic_pause);
        }
    }
});

5.在布局文件中添加一个SeekBar作为进度条,并添加一个全屏切换按钮:

<SeekBar
    android:id="@+id/seek_bar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"/>

<ImageButton
    android:id="@+id/fullscreen_button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/ic_fullscreen"/>

6.在Activity或Fragment中,添加代码来更新和控制进度条:

SeekBar seekBar = findViewById(R.id.seek_bar);

// 更新进度条

player.addListener(new Player.EventListener() {
    @Override
    public void onPositionDiscontinuity(int reason) {
        updateSeekBar();
    }

    @Override
    public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
        updateSeekBar();
    }

    private void updateSeekBar() {
        long duration = player.getDuration();
        long position = player.getCurrentPosition();
        seekBar.setMax((int) duration);
        seekBar.setProgress((int) position);
    }
});

// 控制进度条拖动

seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
        if (fromUser) {
            player.seekTo(progress);
        }
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {
    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
    }
});

7.实现全屏切换功能:

ImageButton fullscreenButton = findViewById(R.id.fullscreen_button);
fullscreenButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
            // 切换到横屏
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        } else {
            // 切换到竖屏
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        }
    }
});

实际开发中,可能还需要处理更多的细节,如旋转屏幕时保持播放状态、适应不同分辨率的设备等。此外,可以根据项目需求添加更多的自定义功能,如弹幕、手势控制等。

6.2 视频编解码

实现视频剪辑、合并和转码等功能,需要对视频编解码有一定了解。可以使用Android提供的MediaCodec类或者第三方库如FFmpeg来实现这些功能。以FFmpeg为例,实现视频剪辑功能:

String inputVideoPath = "输入视频文件路径";
String outputVideoPath = "输出视频文件路径";
String startTime = "00:00:05"; // 剪辑开始时间
String duration = "00:00:10"; // 剪辑持续时间

String[] ffmpegCommand = new String[]{"-i", inputVideoPath, "-ss", startTime, "-t", duration, "-c", "copy", outputVideoPath};

int result = FFmpeg.execute(ffmpegCommand);

6.3 视频流媒体和直播

视频流媒体和直播是当前非常热门的技术,通过学习这方面的知识,可以开发出如直播平台、在线教育、远程会议等应用。
实现视频流媒体和直播功能,通常需要结合第三方库和服务。例如,使用ExoPlayer库和阿里云直播服务实现直播功能:
在阿里云控制台创建一个直播流,并获取推流地址和拉流地址。
在应用中集成阿里云的直播SDK,使用推流地址进行推流。
使用ExoPlayer播放拉流地址。

6.4 视频AI

结合人工智能技术,我们可以实现视频的智能分析和处理,比如人脸识别、物体检测、情感分析等。
实现视频AI功能,可以使用开源库如TensorFlow Lite、OpenCV等。以人脸识别为例,可以使用OpenCV进行实现:
首先,需要在项目中导入OpenCV库。
使用OpenCV的CascadeClassifier类加载预训练的人脸识别模型(如Haar Cascade模型)。
对视频帧进行处理,使用CascadeClassifier.detectMultiScale()方法检测人脸。
根据检测结果,在视频帧上绘制人脸边框。
这些只是针对上述进阶主题的基本实现思路。在实际开发中,可能会遇到更多的技术细节和业务需求。需要不断学习和实践,以满足项目的需求。

标签:...,FFmpeg,视频,MediaPlayer,直播,exoplayer,MediaCodec,new,播放
From: https://blog.csdn.net/hexin5213578/article/details/139499943

相关文章

  • 【compshare】(3):使用UCloud(优刻得)的compshare算力平台,新增加SD-webui和大模型镜像,可
    关于UCloud(优刻得)旗下的compshare算力共享平台https://www.ucloud.cn/UCloud(优刻得)是中国知名的中立云计算服务商,科创板上市(股票代码:688158),中国云计算第一股,专注于提供可靠的企业级云服务,包括云服务器、云主机、云数据库、混合云、CDN、人工智能等服务。compshare......
  • golang 可变参数用法, handlers ...HandlerFunc
     handlers...HandlerFunc这是什么写法,与group.handle()第三个参数是[]handlerFunc是什么关系呢?下面是gin中的用法:routergroup.go//GETisashortcutforrouter.Handle("GET",path,handle).func(group*RouterGroup)GET(relativePathstring,handlers...Ha......
  • js 中 (function($){...})(jQuery) 含义
    原文链接:https://www.cnblogs.com/Jeely/p/10715089.htmljs中(function($){...})(jQuery)含义js中定义函数常用写法是functionname(arg){//arg则是匿名函数的参数。//...}调用函数时的写法是:name(arg);======================================================......
  • MJ伪造图片名(1,2,3...)
    importosdefrename_images_in_folder(root_folder):#遍历根目录forroot,dirs,filesinos.walk(root_folder):ifnotdirs:#检查当前目录下是否有子目录,没有则表示是底层子文件夹count=1forfileinfiles:......
  • 我真的从测试转成了开发......
    写在前面因为走的圈太大了,早上上班差点迟到,幸好有我每日5公里的加持,侥幸踩点进办公室,哈哈,真的好险!我开发的功能不能用了上午开始着手某功能的开发,还在写后台逻辑。结果到了下午,由于前端同学的代码冲突,打包发布后,导致我写的功能直接不能用了,瞬间emo了!套用我之前同事总说的一句......
  • 工具:一键采集 平台:TB+PDD+JD...
    什么是数据集?电商商品数据集通常是指收集自电子商务平台的商品信息的结构化数据集合。这些数据包括但不限于商品名称、价格、描述、用户评价、分类标签、卖家信息、销售量、库存量、图片链接等。数据集可以由电商平台公开提供,也可以通过网络爬虫等技术手段获得,并且经常会用于机......
  • 工具:一键采集 平台:TB+PDD+JD...
    什么是数据集?电商商品数据集通常是指收集自电子商务平台的商品信息的结构化数据集合。这些数据包括但不限于商品名称、价格、描述、用户评价、分类标签、卖家信息、销售量、库存量、图片链接等。数据集可以由电商平台公开提供,也可以通过网络爬虫等技术手段获得,并且经常会用于......
  • 为什么会收到此警告,如何解决 "此版本只能理解 SDK XML 2 以下版本,但 SDK XML...&quot;
    我刚刚更新了我当前的Android应用程序,使其使用java11、构建工具32.0.0和下面是我使用的AndroidStudio的详细信息AndroidStudioBumblebee|2021.1.1Beta5构建号:AI-211.7628.21.2111.7956428,构建于2021年11月30日运行时版本:11.0.11+0-b60-7590822x86_......
  • idea实用快捷键(持续更新...)
    文章目录1、快速输入try/catch/finally2、选中多个光标3、实现接口4、方法参数提示5、查看某个类的子类6、弹出显示查找内容的搜索框1、快速输入try/catch/finallyCtrl+Alt+T2、选中多个光标Shift+Alt+单机多选End可以全部到行尾,Home则可以全部回到行首3、......
  • idea编辑器中 This document contents very long lines..........
    背景:在idea编辑器中引入压缩后的js文件在子目录中,有如下提示在控制台-元素中js并未加载上 另外,同文件夹下未压缩的js文件可正常加载。可以,排除,路径问题。 解决问题:压缩的js,得放到根目录,可能是文件太大了,需要预加载 ......