一、使用说明
- 小米动态照片SDK 基于安卓平台,提供了小米动态照片相关的接口能力,开发者可以根据接口能力,实现自己的动态照片相关的业务功能
- 小米动态照片是由一个Jpeg文件和一个MP4文件合成的特殊结构的文件
二、接入方式
- 开发者于开放平台发起申请->审核通过后咨询客服获取接入所需账号、密码->开发者接入SDK->联调测试->功能上线
- 申请通过后,请咨询客服获取maven或AAR文件的账号及密码
三、调用方法说明
1、maven依赖
repositories {
maven{
url"https://repos.xiaomi.com/maven"
credentials{
username="*************"
password="**************************"
}
}
}
// 正式版本
implementation 'com.xiaomi.camera:livephoto:1.0.0'
2、AAR文件下载到本地引入
AAR下载地址:
https://repos.xiaomi.com/maven/com/xiaomi/camera/livephoto/1.0.0/livephoto-1.0.0.aar
username="*************"
password="**************************"
SDK需要依赖com.adobe.xmp:xmpcore包:
implementation("com.adobe.xmp:xmpcore:5.1.3")
3、权限依赖
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE" />
4、调用方法说明
动态照片SDK 采用静态方式调用,无需初始化。请在调用SDK前确保已获取对应的读写权限
4.1 判断本机设备是否支持动态照片
/**
* 返回本机设备是否支持LivePhoto
*
* @return true=支持,fasle=不支持
*/
public static boolean isDeviceSupportLivePhoto();
调用示例
boolean isSupport = MiLivePhotoTools.isDeviceSupportLivePhoto();
4.2 识别一个文件是否是动态照片
动态照片SDK提供2种方式来识别单个文件是否是Livephoto:
1、通过查询媒体数据库识别。会占用较低的内存,提供更快的识别速度,但是会被系统照明弹记录到访问媒体库的次数
2、通过读取文件头信息识别。会占用IO资源,识别速度取决的设备的IO速度,但是不会被系统照明弹记录到
4.2.1 通过查询媒体数据库识别
/**
* 识别文件是否属于livephoto照片
*
* @param context
* @param path 文件路径
* @return 是否是livephoto类型的照片
*/
public static boolean isLivePhoto(Context context, String path);
/**
* 识别文件是否属于livephoto照片
*
* @param context
* @param 文件Uri
* @return 是否是livephoto类型的照片
*/
public static boolean isLivePhoto(Context context, Uri uri);
调用示例
// path类型参数
boolean isLivephoto = MiLivePhotoTools.isLivePhoto(getBaseContext(), imagePath);
// uri类型参数
boolean isLivephoto = MiLivePhotoTools.isLivePhoto(getBaseContext(), imageUri);
4.2.2 通过文件IO方式,读取文件头信息识别
/**
* 识别是否是Livephoto文件
* 内部通过IO访问文件,寻找文件头中Livephoto相关的xmp信息。
* @param path
* @return
*/
public static boolean isLivePhotoFile(String path);
/**
* 识别是否是Livephoto文件
* 内部通过IO访问文件,寻找文件头中Livephoto相关的xmp信息。
* @param context
* @param uri
* @return
*/
public static boolean isLivePhotoFile(Context context, Uri uri);
调用示例
// path类型参数
boolean isLivephoto = MiLivePhotoTools.isLivePhotoFile(imagePath);
// uri类型参数
boolean isLivephoto = MiLivePhotoTools.isLivePhotoFile(getBaseContext(), imageUri);
4.3 拆解视频和图片部分
/**
* 将动态照片的Jpeg部分和Video拆出来
* 如果jpeg或者Video输入为Null,则不拆解对应的文件。
*
* @param livePhotoPath 输入的Livephoto文件
* @param jpegOutPath 输出的jpeg文件,传Null不输出
* @param videoOutPath 输出的Video部分的文件,传Null不输出
* @return 解码后的Livephoto信息,解码失败时返回Null
*/
public static LivePhotoInfo decodeLivephoto(String livePhotoPath, String jpegOutPath, String videoOutPath);
/**
* 将动态照片的Jpeg部分和Video拆出来
* 如果jpeg或者Video输入为Null,则不拆解对应的文件。
*
* @param context
* @param livePhotoUri 输入的Livephoto文件
* @param jpegOutUri 输出的jpeg文件,传Null不输出
* @param videoOutUri 输出的Video部分的文件,传Null不输出
* @return 解码后的Livephoto信息,解码失败时返回Null
*/
public static LivePhotoInfo decodeLivephoto(Context context, Uri livePhotoUri, Uri jpegOutUri, Uri videoOutUri);
返回一个MiLivePhotoInfo对象,记录了一些解码信息,如下:
/**
* LivePhoto照片的封装对象,包含Livephoto的一些信息。
*/
public class MiLivePhotoInfo {
/**
* 类型
* 1 = 动态照片
* 0 = 非动态照片
*/
private int type;
/**
* 版本
*/
private int version;
/**
* 视频部分相对于图片数据尾部的偏移量,等于视频部分的字节大小
*/
private int videoOffset;
/**
* 表示图片在视频中对应帧的时间戳(单位:微秒)
*/
private long videoPresentationTimestampUs;
public MiLivePhotoInfo(){
}
public MiLivePhotoInfo(int type,int version, int videoOffset, long videoPresentationTimestampUs) {
this.type = type;
this.version = version;
this.videoOffset = videoOffset;
this.videoPresentationTimestampUs = videoPresentationTimestampUs;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
public int getVideoOffset() {
return videoOffset;
}
public void setVideoOffset(int videoOffset) {
this.videoOffset = videoOffset;
}
public long getVideoPresentationTimestampUs() {
return videoPresentationTimestampUs;
}
public void setVideoPresentationTimestampUs(long videoPresentationTimestampUs) {
this.videoPresentationTimestampUs = videoPresentationTimestampUs;
}
}
调用示例
// path类型参数
LivePhotoInfo info = MiLivePhotoTools.decodeLivephoto(srcPath,decodeImagePath,decodeVideoPath);
// uri类型参数
LivePhotoInfo info = MiLivePhotoTools.decodeLivephoto(getBaseContext(),srcUri,decodeImageUri,decodeVideoUri);
4.4 合成动态照片
提供合成动态照片的能力,接口描述如下
其中videoPresentationTimestampUs参数是必传的参数,表示与jpeg对应的视频帧的显示时间戳。如果不确定该值,开发者可以传入0,但是不能保证合成后的照片在相册中正常播放和编辑。如果开发者希望合成后的图能够在小米相册中正确播放和编辑,那么开发者必须传入正确的值
/**
* 根据输入的jpeg图像和Video文件,生成一张Livephoto格式的jpeg文件,输出到outFilePath对应的File中
* 并返回合成是否成功
*
* @param jpegPath 要合成的Jpeg文件
* @param videoPath 要合成的Video文件
* @param videoPresentationTimestampUs jpeg在Video中对应帧的时间戳
* @param outFilePath 合成后的文件路径
* @param insertMediaStore (可选参数)是否插入媒体库,默认true
* @return 合成是否成功
*/
public static boolean muxLivephoto(String jpegPath, String videoPath, long videoPresentationTimestampUs, String outFilePath, boolean insertMediaStore);
/**
* 根据输入的jpeg图像和Video文件,生成一张Livephoto格式的jpeg文件,输出到outFileUri描述的File中
* 并返回合成是否成功
*
* @param context
* @param jpegUri 要合成的Jpeg文件
* @param videoUri 要合成的Video文件
* @param videoPresentationTimestampUs jpeg在Video中对应帧的时间戳
* @param outFileUri 合成后的文件Uri
* @param insertMediaStore (可选参数)是否插入媒体库,默认true
* @return 合成是否成功
*/
public static boolean muxLivephoto(Context context, Uri jpegUri, Uri videoUri, long videoPresentationTimestampUs, Uri outFileUri, boolean insertMediaStore);
调用示例
// path类型参数
boolean succ = MiLivePhotoTools.muxLivephoto(getBaseContext(),jpegPath,videoPath,videoPresentationTimestampUs,imageOutPath);
// uri类型参数
boolean succ = MiLivePhotoTools.muxLivephoto(getBaseContext(),jpegUri,videoUri,videoPresentationTimestampUs,imageOutUri);
4.4.1 合成动图的设备兼容性
- 仅Xiaomi HyperOS系统支持
当前仅Xiaomi HyperOS版本的系统,支持播放三方合成的动图,MIUI系统不识别三方合成的动图,建议三方开发者,先判断本机设备的系统是否满足条件
Xiaomi HyperOS判断方法:
1. 读取系统属性[ro.miui.ui.version.code],取值是816的就是Xiaomi HyperOS
2. 取值小于816的就是MIUI14及之前的版本
- 系统相册的versionCode大于407621,才能支持播放三方合成的动图
包名:com.miui.gallery
versionCode:> 407621
/**
* 判断当前设备是否是Xiaomi HyperOS系统
*
* @return
*/
public static boolean isHyperOs() {
try {
String code = com.xiaomi.exif.SystemProperties.get("ro.miui.ui.version.code");
if (Integer.valueOf(codeStr) >= 816) {
return true;
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
/**
* 返回相册是否支持播放三方的动图
* @param context
* @return
*/
public static boolean isMiuiGallerySupport(Context context) {
try {
PackageInfo packageInfo = context.getPackageManager()
.getPackageInfo("com.miui.gallery", 0);
if (packageInfo != null) {
return packageInfo.versionCode > 407621;
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
4.5 获取设备的全部动图
这里仅提供示例代码,开发者根据自己需求,选择增加分页或者查询更多的字段信息
/**
* 从媒体数据库中查询出所有的动图记录
* @param context
* @return List<String>
*/
public static List < String > listAllLivePhoto(Context context) {
List < String > resultList = new ArrayList();
Cursor cursor = null;
try {
String[] projection = {
MediaStore.Images.Media._ID, MediaStore.Images.Media.DATA, MediaStore.Images.Media.XMP
};
// 查询数据库的XMP字段,并按照16进制搜索是否包含livephoto的相关信息
String selection = "HEX(" + MediaStore.Images.Media.XMP + ") LIKE '%" + stringToHex("MicroVideo") + "%' or " +
"HEX(" + MediaStore.Images.Media.XMP + ") LIKE '%" + stringToHex("MotionPhoto") + "%'";
String[] selectionArgs = null;
cursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, selection, selectionArgs, null);
if (cursor.moveToFirst()) {
do {
String path = cursor.getString(cursor.getColumnIndexOrThrow(projection[1]));
resultList.add(path);
} while (cursor.moveToNext());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null) {
cursor.close();
}
}
return resultList;
}
public static String stringToHex(String str) {
StringBuilder hex = new StringBuilder();
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
hex.append(Integer.toHexString((int) c));
}
return hex.toString();
}
4.6 动态照片的展示
不带水印的动态照片,封面图片和视频的比例是一致的。但是带徕卡水印的动态照片,封面图片和视频的比例不一致,其中视频部分的比例跟封面图片去掉水印部分后的比例是一致的
为了保证播放的封面到视频部分的展示体验,开发者需要自行适配,可参考小米系统相册的展示效果
