转自:https://juejin.im/post/5bec0958e51d454c7d0f9a32

关于ijkplayer这块已验证可以播放音视频,Seek正常。

标红: 文章里RawDataSourceProvider的写法有问题, 对于moov位于mp4最后的文件来说, moov最后一位无法读出, 导致播放失败. 修改如下. 详细提交见: https://github.com/oncealong/ArtPlayer/commit/b1151335915249304bc6119dff6748fe336f747d

    @Overridepublic int readAt(long position, byte[] buffer, int offset, int size) {if (position >= mMediaBytes.length) {ARMLog.d(TAG, "position:%s >= mediaBytesLength", position, mMediaBytes.length);return -1;}int length;if (position + size < mMediaBytes.length) {length = size;} else {length = (int) (mMediaBytes.length - position);if (length > buffer.length)length = buffer.length;}System.arraycopy(mMediaBytes, (int) position, buffer, offset, length);ARMLog.d(TAG, "position:%s, offset:%s, size:%s length:%s", position, offset, size, length);return length;}

前言

本文介绍如何通过系统MediaPlayerIjkPlayerExoPlayer分别播放安卓项目下的Raw或Assets文件夹中的音视频文件。

在某些情况下,我们会把一些音视频文件,如Mp3,Mp4等,直接放在安装包中的Raw或者Assets文件夹里,这些音视频文件可能作为特定场景的提示音,或者视频片头等等。关于Raw和Assets资源文件,这里不作过多讨论,总的来讲,他们都是被打包进APK中的文件,不会被编译成二进制,程序可以直接访问,无需额外的权限。

先说明一下本文代码的构建环境和使用的播放内核版本:

  • Java 1.7
  • Android Studio 3.1.2
  • Gradle 4.4
  • IjkPlayer 0.8.8
  • ExoPlayer 2.8.3

效果演示

Raw/Assets资源文件访问方式

在项目文件夹中的位置:

Raw文件访问方式

  • Raw文件位于res/raw目录下,Raw文件会被映射到R.java文件中,所以访问的时候直接使用资源ID即可,如
R.raw.raw_video
复制代码

或者获得该文件的AssetFileDescriptor:

AssetFileDescriptor afd = getResources().openRawResourceFd(R.raw.raw_video);
复制代码

Assets文件访问方式

  • Assets文件夹下的文件不会被映射到R.java中,访问的时候需要AssetManager类。
AssetManager am = getAssets();
try {AssetFileDescriptor afd= am.openFd(fileName);
} catch (IOException e) {e.printStackTrace();
}
复制代码

AssetFileDescriptor可以理解成访问Raw/Assets文件的一个入口,或者说是一把钥匙。

Raw/Assets文件还有其他的访问方式,比如通过ContentResolver,又或者直接开启一个InputStream去读取文件,这应该是播放器内核需要做的事情,我们只需要给播放器提供以上的信息即可。

········································································································· 下面直接上代码

通过系统MediaPlayer播放音视频

  • Raw文件
//实例化播放内核
android.media.MediaPlayer mediaPlayer = new android.media.MediaPlayer();
//获得播放源访问入口
AssetFileDescriptor afd = getResources().openRawResourceFd(R.raw.raw_video); // 注意这里的区别
//给MediaPlayer设置播放源
mediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
//设置准备就绪状态监听
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {@Overridepublic void onPrepared(MediaPlayer mp) {// 开始播放mediaPlayer.start();}
});
//准备播放
mediaPlayer.prepareAsync();
复制代码
  • Assets 文件
//实例化播放内核
android.media.MediaPlayer mediaPlayer = new android.media.MediaPlayer();
//获得播放源访问入口
AssetManager am = getAssets();
try {AssetFileDescriptor afd = am.openFd("assets_video.mp4");// 注意这里的区别//给MediaPlayer设置播放源mediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
} catch (IOException e) {e.printStackTrace();
}
//设置准备就绪状态监听
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {@Overridepublic void onPrepared(MediaPlayer mp) {// 开始播放mediaPlayer.start();}
});
//准备播放
mediaPlayer.prepareAsync();
复制代码

代码中每一步都有注释,可以说非常详细了。简单总结一下,无论播放Raw文件还是Assets文件,我们首先获得AssetFileDescriptor,然后设置给MediaPlayer。

·········································································································

通过IjkPlayer播放音视频

  • Raw 文件
//实例化播放内核
tv.danmaku.ijk.media.player.IjkMediaPlayer ijkPlayer = new tv.danmaku.ijk.media.player.IjkMediaPlayer();
//获得播放源访问入口
AssetFileDescriptor afd = getResources().openRawResourceFd(R.raw.raw_video); // 注意这里的区别
//构建IjkPlayer能识别的IMediaDataSource,下面的RawDataSourceProvider实现了IMediaDataSource接口
RawDataSourceProvider sourceProvider = new RawDataSourceProvider(fd);
//给IjkPlayer设置播放源
ijkPlayer.setDataSource(sourceProvider);
//设置准备就绪状态监听
ijkPlayer .setOnPreparedListener(new MediaPlayer.OnPreparedListener() {@Overridepublic void onPrepared(MediaPlayer mp) {// 开始播放ijkPlayer.start();}
});
//准备播放
ijkPlayer.prepareAsync();
复制代码
  • Assets 文件
//实例化播放内核
tv.danmaku.ijk.media.player.IjkMediaPlayer ijkPlayer = new tv.danmaku.ijk.media.player.IjkMediaPlayer();
//获得播放源访问入口
AssetManager am = getAssets();
try {AssetFileDescriptor afd = am.openFd("assets_video.mp4");// 注意这里的区别//构建IjkPlayer能识别的IMediaDataSource,下面的RawDataSourceProvider实现了IMediaDataSource接口RawDataSourceProvider sourceProvider = new RawDataSourceProvider(fd);//给IjkPlayer设置播放源ijkPlayer.setDataSource(sourceProvider);
} catch (IOException e) {e.printStackTrace();
}
//设置准备就绪状态监听
ijkPlayer .setOnPreparedListener(new MediaPlayer.OnPreparedListener() {@Overridepublic void onPrepared(MediaPlayer mp) {// 开始播放ijkPlayer.start();}
});
//准备播放
ijkPlayer.prepareAsync();
复制代码

补充下,其中的RawDataSourceProvider实现了IMediaDataSource 接口,IMediaDataSourceIjkPlayer包中的接口,实现了IMediaDataSource接口的类可以设置给IjkPlayer作为播放源。就像下面这样:

import tv.danmaku.ijk.media.player.misc.IMediaDataSource;public class RawDataSourceProvider implements IMediaDataSource {private AssetFileDescriptor mDescriptor;private byte[] mMediaBytes;public RawDataSourceProvider(AssetFileDescriptor descriptor) {this.mDescriptor = descriptor;}@Overridepublic int readAt(long position, byte[] buffer, int offset, int size) {if (position + 1 >= mMediaBytes.length) {return -1;}int length;if (position + size < mMediaBytes.length) {length = size;} else {length = (int) (mMediaBytes.length - position);if (length > buffer.length)length = buffer.length;length--;}// 把文件内容copy到buffer中;System.arraycopy(mMediaBytes, (int) position, buffer, offset, length);return length;}@Overridepublic long getSize() throws IOException {long length = mDescriptor.getLength();if (mMediaBytes == null) {InputStream inputStream = mDescriptor.createInputStream();mMediaBytes = readBytes(inputStream);}return length;}@Overridepublic void close() throws IOException {if (mDescriptor != null)mDescriptor.close();mDescriptor = null;mMediaBytes = null;}//读取文件内容private byte[] readBytes(InputStream inputStream) throws IOException {ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream();int bufferSize = 1024;byte[] buffer = new byte[bufferSize];int len;while ((len = inputStream.read(buffer)) != -1) {byteBuffer.write(buffer, 0, len);}return byteBuffer.toByteArray();}
}复制代码

小结一下,我们首先获取到Raw/Assets文件的AssetFileDescriptor,然后用它去构建一个IMediaDataSource,最后设置给IjkPlayer

·········································································································

通过ExoPlayer播放音视频

  • ExoPlayer播放器内核的实例化有点复杂,这里单独写
//实例化播放内核
TrackSelection.Factory videoTrackSelectionFactory =new AdaptiveTrackSelection.Factory(new DefaultBandwidthMeter());
DefaultTrackSelector mTrackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
boolean preferExtensionDecoders = true;
boolean useExtensionRenderers = true;//是否开启扩展
@DefaultRenderersFactory.ExtensionRendererMode int extensionRendererMode = useExtensionRenderers? (preferExtensionDecoders ? DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER: DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON): DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF;DefaultRenderersFactory rendererFactory = new DefaultRenderersFactory(mAppContext, extensionRendererMode);
DefaultLoadControl loadControl = new DefaultLoadControl();
//工厂方法获得播放器实例
om.google.android.exoplayer2.SimpleExoPlayer exoPlayer = ExoPlayerFactory.newSimpleInstance(rendererFactory, mTrackSelector, loadControl, null);
复制代码
  • Raw 文件
//构建Raw文件播放源--RawResourceDataSource
DataSpec dataSpec = new DataSpec(RawResourceDataSource.buildRawResourceUri(R.raw.raw_video));
RawResourceDataSource rawResourceDataSource = new RawResourceDataSource(this);
try {rawResourceDataSource.open(dataSpec);
} catch (RawResourceDataSource.RawResourceDataSourceException e) {e.printStackTrace();
}
//构建ExoPlayer能识别的播放源--MediaSource
String url = rawDataSource.getUri().toString();
MediaSource mediaSource = ExoSourceManager.newInstance(mAppContext, getHeaders()).getMediaSource(url, false, false, MediaPlayerManager.instance().isLooping(), null);
//给ExoPlayer设置播放源,并准备播放
exoPlayer.prepare(mediaSource);
//让ExoPlayer准备好后就开始播放
exoPlayer.setPlayWhenReady(true);
复制代码
  • Assets 文件
//构建ExoPlayer能识别的播放源--MediaSource
String url = "file:///android_asset/" + "assets_video.mp4";
MediaSource mediaSource = ExoSourceManager.newInstance(mAppContext, getHeaders()).getMediaSource(url, false, false, MediaPlayerManager.instance().isLooping(), null);
//给ExoPlayer设置播放源,并准备播放
exoPlayer.prepare(mediaSource);
//让ExoPlayer准备好后就开始播放
exoPlayer.setPlayWhenReady(true);
复制代码

小结:ExoPlayer实例化的时候复杂一点,需要按照官方文档一步步来。播放Raw文件的时候,需要先构建出RawResourceDataSource,这是ExoPlayer为了播放Raw音视频文件提供的类,然后获取其中的Uri构建MediaSource,最后设置给ExoPlayer。播放Assets文件就更简单了,形如String url = "file:///android_asset/" + "filename"的Assets文件播放地址可以直接用来构建MediaSource

项目地址:github.com/maiwenchang… 欢迎各位Star~~

[Android多媒体技术] 播放Raw/Assets音视频方法总结相关推荐

  1. FFmpeg Android 学习(一):Android 如何调用 FFMPEG 编辑音视频

    一.概述 在Android开发中,我们对一些音视频的处理比较无力,特别是编辑音视频这部分.而且在Android上对视频编辑方面,几乎没有任何API做支持,MediaCodec(硬编码)也没有做支持.那 ...

  2. 各种RTMP直播流播放权限_音视频_数据花屏_问题检测与分析工具EasyRTMPClient

    之前的一篇博客<网络摄像机IPCamera RTSP直播播放网络/权限/音视频数据/花屏问题检测与分析助手EasyRTSPClient>,我们介绍了RTSP流的检测和分析工具EasyRTS ...

  3. 视频教程-Android WebRTC 实现1V1实时音视频通信-Android

    Android WebRTC 实现1V1实时音视频通信 从2012年开始从事移动互联网方面的开发工作,曾担任去哪儿网开发工程师,搜狗高级开发工程师,拥有多年一线实战开发经验. 擅长语言:Object- ...

  4. 音视频从入门到精通——FFmpeg 播放器实现音视频同步的三种方式

    老人们经常说,播放器对音频和视频的播放没有绝对的静态的同步,只有相对的动态的同步,实际上音视频同步就是一个"你追我赶"的过程. 音视频的同步方式有 3 种,即:音视频分别向系统时钟 ...

  5. Android WebView加载H5音视频自动播放、关闭Activity停止播放

    在Android加载H5,实现H5中的音视频自动播放  在Activity中添加代码: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELL ...

  6. Android VideoView播放 项目中的 视频文件 自动横屏 全屏播放

    记录最近遇到的一个android播放视频的需求: APP主页上方有一个操作演示字样的入口,点击后开始播放一段视频,视频文件是放在工程里的. 话说以前还没做过类似播放视频的功能,根据以往的经验来看,最简 ...

  7. C++实战手把手教您用ffmpeg和QT开发播放器--01音视频基础知识

    笔记:https://blog.csdn.net/tainjau/article/category/9272757 音视频基础知识 封装.解码.重采样.像素格式 1.MPEG-4 MPEG-4标准将众 ...

  8. 都2021了作为一名Android开发者,还不学音视频开发?我劝你早点认清现实!

    缘起 最近经常遇到一些同学问我如何学习音视频,怎样才能快速上手?还有一些对音视频不了解的同学问我该不该学习音视频?作为一名音视频行业的10年Android老兵,我有一些思考分享给大家,希望能对你有所帮 ...

  9. android html5播放器,android Html5播放器混音解决方案

    背景 当一个用户正在听音乐而另一个应用需要通知用户一些重要的事情时,用户可能由于音乐声音大而不能听的通知.从Android2.2开始,平台为应用提供了一个协商它们如何使用设备音频输出的途径,这个机制叫 ...

最新文章

  1. java中的数据库事务处理
  2. 三极管的耐压与hFE之间是什么关系?
  3. python的数组和元组区别_python中数组,列表,元组的区别、定义、功能
  4. [ios2]iOS 使用subversion管理iOS源代码 【转】
  5. Memcached的配置,SSH项目中的整合(com.whalin),Memcached工具类,Memcached的代码调用
  6. 获取服务器信息info
  7. python怎么处理文字_python 处理给规范的文字
  8. UITextField 文本字段控件 -- IOS (解决键盘遮住View及密文設定的问题)(实例)
  9. 博文视点大讲堂41期-SEO难点之网站内部链接结构
  10. 帆软分组合并字符串、提取字符串中的数字、判断多项字符串至少一项被包含
  11. mysql迁移到mysqli_php – 从mysql连接迁移到mysqli
  12. Windows 环境搭建Redis集群之无脑教程
  13. python 地震数据可视化
  14. 买卖股票的最佳时机(第一版)
  15. Jsoncpp 使用说明
  16. scrapy 出现400 Bad Request 问题
  17. 贼法,要想打好打高,几条建议
  18. CTFHub_历年真题_MISC——“图片修复”、“磁盘恢复”、“蛛丝马迹”
  19. 使用UltraISO制作Ubuntu16.04 U盘启动盘
  20. php smart str,致命错误:ext/standard/php_smart_str.h:没有那个文件或目录

热门文章

  1. Photoshop CS4验证序列号无效之破解
  2. 基于paddleocr的字符识别
  3. 北信源vrv杀毒(单机版) 2005版 下载
  4. 深度调研前端框架Vue、React、Angular、jQuery,附参考模板!
  5. 调用系统自带的程序android,Android怎么打开和调用系统自带的程序示例(06)
  6. Windows上安装ROS教程
  7. 关于Git中代码代码push出现index. lock问题
  8. 使用iTextSharp 导出PDF 详解(转)
  9. PhotonServer的下载与配置
  10. IDEA安装阿里代码规范插件(亲测规范部分)