一般获取视频、音频等媒体文件的元数据信息,我们会用android自带的MediaMetadataRetriever这个类。
比如,获取视频的标题、时长、某一帧的截图(常用于视频的缩略图)等信息。
具体用法参考:Android之使用MediaMetadataRetriever类获取视频第一帧
但是…
凡事总不算那么一帆风顺的,如果你像我一样幸运的话,会在某些机型,某些API版本上成功获取缩略图,而在有些机型和低版本的API上报出下面的错误:
MediaMetadataRetrieverJNI: getFrameAtTime: videoFrame is a NULL pointer
查询了许久都木有解决啊,这个问题,够够的~
后来,在一个地方看到讨论说,这个问题可能是android本身的一个bug…好吧,可能我撞了南墙才会回头吧~
要想彻底解决可以换一个库,使用强大的FFmpegMediaMetadataRetriever
那就请follow我的脚步,来看看如何用吧…
开源地址:FFmpegMediaMetadataRetriever的github地址
还有个备份的神马鬼地址:FFmpegMediaMetadataRetriever的备份github地址
我就以第一个正式的开源地址为例好了。
点开上面的开源地址,你会看到下面的页面。
这是github上说的配置方式。
经本人验证,第一句Add the following maven dependency to your project’s build.gradle file是错的!!!
妹的,坑得我不轻…不是在project项目的build.gradle里添加compile那一句,而是在app的build.gradle的dependencies里加compile 'com.github.wseemann:FFmpegMediaMetadataRetriever:1.0.14'
!!!
这样就行了,就集成好了FFmpegMediaMetadataRetriever。
下面那一块呢?就是
Optionally, to support individual ABIs:
dependencies {
compile 'com.github.wseemann:FFmpegMediaMetadataRetriever-armeabi:1.0.14'
compile 'com.github.wseemann:FFmpegMediaMetadataRetriever-armeabi-v7a:1.0.14'
compile 'com.github.wseemann:FFmpegMediaMetadataRetriever-x86:1.0.14'
compile 'com.github.wseemann:FFmpegMediaMetadataRetriever-mips:1.0.14'
compile 'com.github.wseemann:FFmpegMediaMetadataRetriever-x86_64:1.0.14'
compile 'com.github.wseemann:FFmpegMediaMetadataRetriever-arm64-v8a:1.0.14'
}
这一块儿是可选项,适配不同物理架构的机型的,比如开发的应用只给x86的手机用那就添加x86那个依赖就行了。上面我们那个不带架构类型的应该是通通适配的干活。
注意:这个方法的弊端只有一个,那就是最后的安装包会很大,会多出十多M吧哈哈哈,好用是要付出代价的。如果想小一点,就只视频某个机型好了
如果你的android studio下载不了依赖,上面的compile方式用不了,还可以下载它的aar包,类似java的jar包。
还是上面的开源github地址,找到Prebuilt AARs点击下载,会得到一个压缩包,解压后如下图:
看名字就知道和compile是对应的,第一个all-fmmr.aar就是匹配所有架构手机的,剩下的是匹配对应架构的。看aar包的大小也能知道光第一个aar包就近23M多了,出来的安装包能不大嘛。
啥?不会在android studio里使用aar?
好吧,送佛送到西,以集成all-fmmr.aar为例
compile(name:'all-fmmr',ext:'aar')
repositories {
flatDir {
dirs 'libs' // aar目录
}
}
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
defaultConfig {
applicationId "com.example.fxjzzyo.helloworld"
minSdkVersion 15
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
sourceSets{
main{
jniLibs.srcDirs=['libs']
}
}
repositories {
flatDir {
dirs 'libs' // aar目录
}
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
androidTestCompile('com.android.support:support-annotations:26.1.0') {
force = true;
}
compile(name:'all-fmmr',ext:'aar')
// compile 'com.github.wseemann:FFmpegMediaMetadataRetriever:1.0.14'
}
这样,你就可以愉快地在代码里使用FFmpegMediaMetadataRetriever 啦!
正如github上介绍的代码就是
//new出对象
FFmpegMediaMetadataRetriever mmr = new FFmpegMediaMetadataRetriever();
//设置数据源
mmr.setDataSource(mUri);
//获取媒体文件的专辑标题
mmr.extractMetadata(FFmpegMediaMetadataRetriever.METADATA_KEY_ALBUM);
//获取媒体文件的专辑艺术家
mmr.extractMetadata(FFmpegMediaMetadataRetriever.METADATA_KEY_ARTIST);
//获取2秒处的一帧图片(这里的2000000是微秒!!!)
Bitmap b = mmr.getFrameAtTime(2000000, FFmpegMediaMetadataRetriever.OPTION_CLOSEST);
//释放资源
mmr.release();
其实这个和Android自带的MediaMetadataRetriever用法一样一样的,就是多了个l类名变成了FFmpegMediaMetadataRetriever,其余都一样。
听说不贴完整代码的不厚道,我那么厚道当然贴一贴啦。惊喜吧~
public class VideoInfoActivity extends AppCompatActivity {
private ImageView imageView;
//这个网络视频url可能失效,很可能!所以你要自己找一个可以浏览器种打开播放的url
String url = "http://219.238.7.67/mp4files/418000000CE88BD1/202.108.250.226/youku/657259485123b717b789144ef/03000801005C009A1DA6539003E880F6C38754-67FB-4855-9645-7E00B1822316.mp4";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_video_info);
//android 6.0权限申请
checkPermission();
}
public void checkPermission(){
//检查权限(NEED_PERMISSION)是否被授权 PackageManager.PERMISSION_GRANTED表示同意授权
if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
//用户已经拒绝过一次,再次弹出权限申请对话框需要给用户一个解释
if (ActivityCompat.shouldShowRequestPermissionRationale(this, android.Manifest.permission
.WRITE_EXTERNAL_STORAGE)) {
Toast.makeText(this, "请开通相关权限,否则无法正常使用本应用!", Toast.LENGTH_SHORT).show();
}
//申请权限
ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0);
} else {
Toast.makeText(this, "授权成功!", Toast.LENGTH_SHORT).show();
//初始化
init();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if(requestCode==0){
if(grantResults.length>0&&grantResults[0]==PackageManager.PERMISSION_GRANTED){
//申请成功
init();
}else {
Toast.makeText(this,"相机权限申请被拒绝!",Toast.LENGTH_SHORT).show();
}
return;
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
private void init() {
imageView = findViewById(R.id.iv_thumbnail);
}
/**
* 点击按钮,获取视频缩略图
* @param view
*/
public void getVideoInfo(View view) {
Bitmap videoThumbnail = null;
//获取本地视频缩略图,在sdk根目录下准备一个test.mp4的文件
// String videoPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/test.mp4";
// videoThumbnail = getVideoThumbnail(videoPath);
//获取网络视频缩略图
videoThumbnail = getNetVideoThumbnail(url);
imageView.setImageBitmap(videoThumbnail);
}
/**
* 获取本地视频缩略图
* @param filePath
* @return
*/
public Bitmap getVideoThumbnail(String filePath) {
Bitmap b=null;
//使用MediaMetadataRetriever
// MediaMetadataRetriever retriever = new MediaMetadataRetriever();
//FFmpegMediaMetadataRetriever
FFmpegMediaMetadataRetriever retriever = new FFmpegMediaMetadataRetriever();
File file = new File(filePath);
try {
retriever.setDataSource(file.getPath());
b=retriever.getFrameAtTime();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (RuntimeException e) {
e.printStackTrace();
} finally {
try {
retriever.release();
} catch (RuntimeException e) {
e.printStackTrace();
}
}
return b;
}
/**
* 获取网络视频缩略图
* @param url
* @return
*/
public Bitmap getNetVideoThumbnail(String url) {
Bitmap b=null;
//使用MediaMetadataRetriever
// MediaMetadataRetriever retriever = new MediaMetadataRetriever();
//FFmpegMediaMetadataRetriever
FFmpegMediaMetadataRetriever retriever = new FFmpegMediaMetadataRetriever();
try {
retriever.setDataSource(url,new HashMap());
b=retriever.getFrameAtTime(400*1000,FFmpegMediaMetadataRetriever.OPTION_CLOSEST);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (RuntimeException e) {
e.printStackTrace();
} finally {
try {
retriever.release();
} catch (RuntimeException e) {
e.printStackTrace();
}
}
return b;
}
}
参考文章:
https://blog.csdn.net/u012561176/article/details/47858099
https://blog.csdn.net/u010499721/article/details/50338623