- 作者:老汪软件技巧
- 发表时间:2024-09-12 04:01
- 浏览量:
鸿蒙视频封面获取前言
最近鸿蒙的功能做的差不多了,又有时间写写文章了,这篇文章要写的就是如何从鸿蒙本地视频里获取封面。
获取视频
我这是直接从相册获取获取视频,然后处理拿到封面的,前面也有写过获取视频的,这里再写一下吧:
/**
* 相册选取照片
*
* @param args 控制参数
* @param callback H5侧回调函数
*/
pickMedia(args: Array<number>): Promise<string> {
return new Promise((resolve, reject) => {
try {
// 控制参数
let type = args[0] as number;
let num = args[1] as number;
// 图片控制参数
let fileType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;
if (type == 1) {
fileType = photoAccessHelper.PhotoViewMIMETypes.VIDEO_TYPE;
} else if (type == 2) {
fileType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_VIDEO_TYPE;
}
let options = new photoAccessHelper.PhotoSelectOptions()
options.MIMEType = fileType;
options.maxSelectNumber = num
// 通过系统相册选取
this.openGallery(options, (urls) => {
LogUtil.d('pickPhoto successful: ' + urls);
resolve(JSON.stringify(urls))
}, (error) => {
LogUtil.e('pickPhoto error: ' + error);
reject(error)
})
} catch (e) {
LogUtil.e(e)
reject(e)
}
});
}
/**
* 打开相册选取图片、视频
*
* @param options 控制参数
* @param onSuccess 成功回调
* @param onFailed 失败回调
*/
async openGallery(
options: photoAccessHelper.PhotoSelectOptions,
onSuccess: (uris: Array<string>)=> void,
onFailed: (error: string)=>void
) {
try {
let photoPicker = new photoAccessHelper.PhotoViewPicker()
photoPicker.select(options)
.then((PhotoSelectResult: photoAccessHelper.PhotoSelectResult) => {
// file://media/Photo/2/IMG_1713776063_001/screenshot_20240422_165243.jpg
if (PhotoSelectResult.photoUris.length > 0) {
onSuccess(PhotoSelectResult.photoUris)
}
}).catch((err: BusinessError) => {
onFailed(err.message)
})
} catch (error) {
let err: BusinessError = error as BusinessError
onFailed(err.message)
}
}
复制到私有目录
获取视频封面这个功能调了好久,最后不知道怎么就好了,感觉还是得将数据复制到私有目录才能生效:
/**
* 复制文件到私有缓存目录
*
* @param path 原路径
* @param type 类型
* @param context 上下文
* @returns 新路径
*/
async copyFileToCache(path: string, type: string, context: Context): Promise<string>{
return new Promise(async (resolve, reject) => {
try {
let file = await fs.open(path, fs.OpenMode.READ_ONLY);
let fileName = file.name;
if (file){
let dir = context.cacheDir + '/' + type
if (!fs.accessSync(dir)) {
fs.mkdirSync(dir)
}
let newPath = context.cacheDir + `/${type}/${fileName}`
await fs.copyFile(file.fd, newPath)
LogUtil.d("copyFileToCache newPath: " + newPath)
resolve(newPath)
}
reject("open file fail")
} catch (err){
LogUtil.d("copyFileToCache err: " + err.message)
reject(err.message)
}
});
}
取视频封面
这里步骤有点长,一步一步来,我这先写了个入口,首先是复制到私有目录,再获取第一帧作为封面,如果没获取到再做个默认的:
// 获取视频封面
private async getVideoImage(uri: string): Promise<string> {
let path = "";
try {
let mediaUtil = new MediaUtils();
let transfer = new FileTransferUtil()
// 需要先将文件复制到APP私有目录
let localPath = await transfer.copyFileToCache(uriPath, 'video', this.context)
LogUtil.d("getVideoImage localPath: " + localPath)
// 先获取视频第一帧
path = await mediaUtil.getVideoImage(this.context, localPath);
LogUtil.d("getVideoImage path: " + path);
// 默认图片
if (path == "") {
path = await this.getDefaultVideoImage();
LogUtil.d("getVideoImage getDefaultVideoImage: " + path);
}
} catch (e) {
LogUtil.d("getVideoImage error: " + e);
}
return path;
}
下面着重看下如何获取第一帧:
/**
* 获取视频封面
*
* @param context 上下文
* @param uri 视频路径
*/
async getVideoImage(context: Context, localPath: string): Promise<PixelMap> {
// 创建AVImageGenerator对象
let avImageGenerator: media.AVImageGenerator = await media.createAVImageGenerator()
// 设置fdSrc
let file = fs.openSync(localPath, fs.OpenMode.READ_ONLY);
// 设置fdSrc
let avFileDescriptor: media.AVFileDescriptor = {
fd: file.fd
};
avImageGenerator.fdSrc = avFileDescriptor;
// 初始化入参
let timeUs = 0
let queryOption = media.AVImageQueryOptions.AV_IMAGE_QUERY_NEXT_SYNC
let param: media.PixelMapParams = {
width : 300,
height : 300
}
// 获取缩略图(promise模式)
let pixelMap = await avImageGenerator.fetchFrameByTime(timeUs, queryOption, param);
LogUtil.d("getVideoImage pixelMap: " + JSON.stringify(pixelMap))
// 释放资源(promise模式)
avImageGenerator.release()
// 保存到文件
return await this.savePixelMap(context, pixelMap);
// 直接返回
return pixelMap
}
有点长,不过也就是从视频里面获取第一帧的PixelMap,实际就算Android的bitmap嘛,这里好坑官方demo只有resource资源的,说文件fd可以,但是没例子。
当然如果要把这个bitmap保存到文件,可以参考下面代码:
/**
* 保存pixelMap,返回路径
* @param pm
* @returns
*/
private async savePixelMap(context: Context, pm: PixelMap): Promise<string> {
if (pm === null) {
LogUtil.e('传入的pm为空');
return '';
}
try {
return await this.packToFile(context, pm);
} catch (err) {
LogUtil.e("TAG", '保存文件失败,err=' + JSON.stringify(err));
return '';
}
}
private async packToFile(context: Context, pixelMap: PixelMap): Promise<string> {
let fPath: string = context.cacheDir + '/image/' + this.getTimeStr() + '.jpg';
let writeFd: fs.File = await fs.open(fPath, fs.OpenMode.CREATE | fs.OpenMode.READ_WRITE);
let opts : image.PackingOption = { format: "image/jpeg", quality: 100};
const imagePacker = image.createImagePacker();
await imagePacker.packToFile(pixelMap, writeFd.fd, opts);
fs.closeSync(writeFd.fd);
return fPath;
}
private getTimeStr() {
const now: Date = new Date();
const year: number = now.getFullYear();
const month: number = now.getMonth() + 1;
const day: number = now.getDate();
const hours: number = now.getHours();
const minutes: number = now.getMinutes();
const seconds: number = now.getSeconds();
return `${year}${month}${day}_${hours}${minutes}${seconds}`;
}
拿到bitmap或者文件路径,就可以在页面中显示,或者上传了。
小结
简单写了下从视频文件获取,视频第一帧图片获取,PixelMap(bitmap)保存到文件,方便页面显示或者封面文件上传。