Harmony学习之多媒体处理
一、场景引入
小明正在开发一个社交应用,需要实现拍照、录制视频、播放音乐等功能。他发现多媒体处理是移动应用开发的核心能力之一,涉及相机调用、音视频录制播放、图片选择编辑等多个方面。在HarmonyOS中,多媒体子系统提供了丰富的API来支持这些功能。
二、核心概念
2.1 多媒体子系统架构
HarmonyOS多媒体子系统采用分层架构设计,核心包括:
- 媒体播放器(AVPlayer):负责音视频播放管理
- 媒体录制器(AVRecorder):提供音视频录制能力
- 屏幕录制(AVScreenCapture):支持屏幕录制功能
- 相机服务(CameraKit):相机硬件访问和操作
- 音频服务(OHAudio):音频采集和渲染
2.2 权限体系
多媒体功能需要申请相应权限:
| 权限名称 | 权限说明 | 使用场景 |
|---|---|---|
| ohos.permission.CAMERA | 相机权限 | 拍照、录像 |
| ohos.permission.MICROPHONE | 麦克风权限 | 录音、视频录制 |
| ohos.permission.READ_MEDIA | 读取媒体文件 | 访问相册 |
| ohos.permission.WRITE_MEDIA | 写入媒体文件 | 保存图片视频 |
三、相机开发
3.1 相机初始化
相机开发需要先创建相机实例并配置参数:
// entry/src/main/ets/common/utils/CameraManager.ts
import camera from '@kit.CameraKit';
import { BusinessError } from '@ohos.base';
export class CameraManager {
private cameraKit: camera.CameraKit;
private cameraDevice: camera.CameraDevice | null = null;
private context: common.UIAbilityContext;
constructor(context: common.UIAbilityContext) {
this.context = context;
this.cameraKit = camera.getCameraManager();
}
// 获取相机列表
async getCameraList(): Promise<string[]> {
try {
const cameraIds = this.cameraKit.getCameraIds();
return cameraIds;
} catch (error) {
console.error('获取相机列表失败:', error);
return [];
}
}
// 创建相机实例
async createCamera(cameraId: string): Promise<boolean> {
try {
this.cameraDevice = await this.cameraKit.createCamera(
cameraId,
this.createCameraStateCallback(),
this.createEventHandler()
);
return true;
} catch (error) {
console.error('创建相机失败:', error);
return false;
}
}
// 相机状态回调
private createCameraStateCallback(): camera.CameraStateCallback {
return {
onCreated: (camera: camera.Camera) => {
console.log('相机创建成功');
this.configureCamera(camera);
},
onConfigured: (camera: camera.Camera) => {
console.log('相机配置完成');
this.startPreview(camera);
},
onReleased: (camera: camera.Camera) => {
console.log('相机已释放');
}
};
}
// 配置相机参数
private configureCamera(camera: camera.Camera) {
const configBuilder = camera.getCameraConfigBuilder();
const config = configBuilder
.setPreviewFrameRateRange([30, 30])
.setPreviewSize({ width: 1920, height: 1080 })
.build();
camera.configure(config);
}
// 开始预览
private startPreview(camera: camera.Camera) {
const frameConfigBuilder = camera.getFrameConfigBuilder(
camera.FrameConfigType.FRAME_CONFIG_PREVIEW
);
const frameConfig = frameConfigBuilder
.addSurface(this.createPreviewSurface())
.build();
camera.triggerLoopingCapture(frameConfig);
}
// 创建预览Surface
private createPreviewSurface(): Surface {
// 创建预览Surface并返回
return new Surface();
}
// 释放相机资源
async releaseCamera() {
if (this.cameraDevice) {
await this.cameraDevice.release();
this.cameraDevice = null;
}
}
}
3.2 拍照功能
// entry/src/main/ets/common/utils/CameraManager.ts
export class CameraManager {
// 拍照
async takePhoto(): Promise<image.PixelMap | null> {
if (!this.cameraDevice) {
console.error('相机未初始化');
return null;
}
try {
const frameConfigBuilder = this.cameraDevice.getFrameConfigBuilder(
camera.FrameConfigType.FRAME_CONFIG_PHOTO_OUTPUT
);
const imageReceiver = image.createImageReceiver(
1920, 1080, image.ImageFormat.JPEG, 8
);
const frameConfig = frameConfigBuilder
.addSurface(imageReceiver.getReceivingSurface())
.build();
// 触发单帧捕获
await this.cameraDevice.triggerSingleCapture(frameConfig);
// 获取拍照结果
const imageArray = await imageReceiver.readNextImage();
const pixelMap = await imageArray.createPixelMap();
return pixelMap;
} catch (error) {
console.error('拍照失败:', error);
return null;
}
}
// 保存图片到相册
async savePhotoToGallery(pixelMap: image.PixelMap, fileName: string): Promise<boolean> {
try {
const imagePacker = image.createImagePacker();
const imageData = await imagePacker.packing(pixelMap, {
format: 'image/jpeg',
quality: 90
});
const mediaLibrary = mediaLibraryKit.getMediaLibrary(this.context);
const publicPath = await mediaLibrary.getPublicDirectory(
mediaLibraryKit.DirectoryType.DIR_IMAGE
);
const imageAsset = await mediaLibrary.createAsset(
mediaLibraryKit.MediaType.IMAGE,
fileName,
publicPath
);
const file = await imageAsset.open(mediaLibraryKit.OpenMode.WRITE_ONLY);
await fileIo.write(file.fd, imageData);
await imageAsset.close(file.fd);
return true;
} catch (error) {
console.error('保存图片失败:', error);
return false;
}
}
}
四、音视频录制
4.1 音频录制
// entry/src/main/ets/common/utils/AudioRecorder.ts
import media from '@kit.MediaKit';
import fileIo from '@kit.CoreFileKit';
import { BusinessError } from '@ohos.base';
export class AudioRecorder {
private avRecorder: media.AVRecorder | null = null;
private filePath: string = '';
private fd: number = 0;
// 开始录音
async startRecord(context: common.UIAbilityContext): Promise<boolean> {
try {
// 创建录音文件
this.filePath = context.filesDir + '/record_' + Date.now() + '.m4a';
const file = fileIo.openSync(this.filePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE);
this.fd = file.fd;
// 配置录音参数
const config: media.AVRecorderConfig = {
audioSourceType: media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC,
profile: {
audioBitrate: 100000,
audioChannels: 1,
audioCodec: media.CodecMimeType.AUDIO_AAC,
audioSampleRate: 48000,
fileFormat: media.ContainerFormatType.CFT_MPEG_4A
},
url: `fd://${this.fd}`
};
// 创建录制器
this.avRecorder = await media.createAVRecorder();
await this.avRecorder.prepare(config);
await this.avRecorder.start();
return true;
} catch (error) {
console.error('开始录音失败:', error);
return false;
}
}
// 停止录音
async stopRecord(): Promise<string> {
try {
if (this.avRecorder) {
await this.avRecorder.stop();
await this.avRecorder.release();
this.avRecorder = null;
}
if (this.fd) {
fileIo.closeSync(this.fd);
}
return this.filePath;
} catch (error) {
console.error('停止录音失败:', error);
return '';
}
}
// 暂停录音
async pauseRecord(): Promise<void> {
if (this.avRecorder) {
await this.avRecorder.pause();
}
}
// 恢复录音
async resumeRecord(): Promise<void> {
if (this.avRecorder) {
await this.avRecorder.resume();
}
}
}
4.2 视频录制
// entry/src/main/ets/common/utils/VideoRecorder.ts
import media from '@kit.MediaKit';
import fileIo from '@kit.CoreFileKit';
import { BusinessError } from '@ohos.base';
export class VideoRecorder {
private avRecorder: media.AVRecorder | null = null;
private filePath: string = '';
private fd: number = 0;
// 开始录像
async startRecord(context: common.UIAbilityContext): Promise<boolean> {
try {
// 创建录像文件
this.filePath = context.filesDir + '/video_' + Date.now() + '.mp4';
const file = fileIo.openSync(this.filePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE);
this.fd = file.fd;
// 配置录像参数
const config: media.AVRecorderConfig = {
audioSourceType: media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC,
videoSourceType: media.VideoSourceType.VIDEO_SOURCE_TYPE_SURFACE_YUV,
profile: {
audioBitrate: 128000,
audioChannels: 2,
audioCodec: media.CodecMimeType.AUDIO_AAC,
audioSampleRate: 48000,
videoBitrate: 2000000,
videoCodec: media.CodecMimeType.VIDEO_AVC,
videoFrameWidth: 1280,
videoFrameHeight: 720,
videoFrameRate: 30,
fileFormat: media.ContainerFormatType.CFT_MPEG_4
},
url: `fd://${this.fd}`
};
// 创建录制器
this.avRecorder = await media.createAVRecorder();
await this.avRecorder.prepare(config);
await this.avRecorder.start();
return true;
} catch (error) {
console.error('开始录像失败:', error);
return false;
}
}
// 停止录像
async stopRecord(): Promise<string> {
try {
if (this.avRecorder) {
await this.avRecorder.stop();
await this.avRecorder.release();
this.avRecorder = null;
}
if (this.fd) {
fileIo.closeSync(this.fd);
}
return this.filePath;
} catch (error) {
console.error('停止录像失败:', error);
return '';
}
}
}
五、音视频播放
5.1 音频播放
// entry/src/main/ets/common/utils/AudioPlayer.ts
import media from '@kit.MediaKit';
import { BusinessError } from '@ohos.base';
export class AudioPlayer {
private avPlayer: media.AVPlayer | null = null;
private isPlaying: boolean = false;
// 播放音频
async playAudio(url: string): Promise<boolean> {
try {
this.avPlayer = await media.createAVPlayer();
// 监听播放状态
this.avPlayer.on('stateChange', (state: string) => {
console.log('播放状态:', state);
if (state === 'prepared') {
this.avPlayer?.play();
this.isPlaying = true;
}
});
// 监听播放完成
this.avPlayer.on('playbackComplete', () => {
console.log('播放完成');
this.isPlaying = false;
});
// 设置播放地址
this.avPlayer.url = url;
await this.avPlayer.prepare();
return true;
} catch (error) {
console.error('播放音频失败:', error);
return false;
}
}
// 暂停播放
pause(): void {
if (this.avPlayer && this.isPlaying) {
this.avPlayer.pause();
this.isPlaying = false;
}
}
// 继续播放
resume(): void {
if (this.avPlayer && !this.isPlaying) {
this.avPlayer.play();
this.isPlaying = true;
}
}
// 停止播放
stop(): void {
if (this.avPlayer) {
this.avPlayer.stop();
this.avPlayer.release();
this.avPlayer = null;
this.isPlaying = false;
}
}
// 跳转到指定位置
seekTo(position: number): void {
if (this.avPlayer) {
this.avPlayer.seek(position);
}
}
}
5.2 视频播放
// entry/src/main/ets/pages/VideoPlayerPage.ets
import media from '@kit.MediaKit';
import { BusinessError } from '@ohos.base';
@Entry
@Component
struct VideoPlayerPage {
@State isPlaying: boolean = false;
@State currentTime: number = 0;
@State duration: number = 0;
private avPlayer: media.AVPlayer | null = null;
// 播放视频
async playVideo(url: string) {
try {
this.avPlayer = await media.createAVPlayer();
// 监听播放状态
this.avPlayer.on('stateChange', (state: string) => {
if (state === 'prepared') {
this.duration = this.avPlayer?.duration || 0;
this.avPlayer?.play();
this.isPlaying = true;
}
});
// 监听播放进度
this.avPlayer.on('timeUpdate', (currentTime: number) => {
this.currentTime = currentTime;
});
// 设置播放地址
this.avPlayer.url = url;
await this.avPlayer.prepare();
} catch (error) {
console.error('播放视频失败:', error);
}
}
// 暂停/继续播放
togglePlay() {
if (this.isPlaying) {
this.avPlayer?.pause();
this.isPlaying = false;
} else {
this.avPlayer?.play();
this.isPlaying = true;
}
}
// 释放资源
aboutToDisappear() {
if (this.avPlayer) {
this.avPlayer.release();
this.avPlayer = null;
}
}
build() {
Column() {
// 视频播放器
Video({
src: this.avPlayer?.url || '',
controller: this.avPlayer
})
.width('100%')
.height(300)
// 播放控制
Row() {
Button(this.isPlaying ? '暂停' : '播放')
.onClick(() => this.togglePlay())
Slider({
value: this.currentTime,
min: 0,
max: this.duration,
step: 1
})
.onChange((value: number) => {
this.avPlayer?.seek(value);
})
}
}
}
}
六、相册图片选择
6.1 选择单张图片
// entry/src/main/ets/common/utils/PhotoPicker.ts
import photoAccessHelper from '@kit.MediaLibraryKit';
import { BusinessError } from '@ohos.base';
export class PhotoPicker {
// 选择单张图片
static async selectSingleImage(): Promise<string | null> {
try {
const photoPicker = new photoAccessHelper.PhotoViewPicker();
const photoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;
photoSelectOptions.maxSelectNumber = 1;
const result = await photoPicker.select(photoSelectOptions);
if (result.photoUris.length > 0) {
return result.photoUris[0];
}
return null;
} catch (error) {
console.error('选择图片失败:', error);
return null;
}
}
// 选择多张图片
static async selectMultipleImages(maxCount: number = 9): Promise<string[]> {
try {
const photoPicker = new photoAccessHelper.PhotoViewPicker();
const photoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;
photoSelectOptions.maxSelectNumber = maxCount;
const result = await photoPicker.select(photoSelectOptions);
return result.photoUris;
} catch (error) {
console.error('选择图片失败:', error);
return [];
}
}
// 选择视频
static async selectVideo(): Promise<string | null> {
try {
const photoPicker = new photoAccessHelper.PhotoViewPicker();
const photoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.VIDEO_TYPE;
photoSelectOptions.maxSelectNumber = 1;
const result = await photoPicker.select(photoSelectOptions);
if (result.photoUris.length > 0) {
return result.photoUris[0];
}
return null;
} catch (error) {
console.error('选择视频失败:', error);
return null;
}
}
}
6.2 图片预览组件
// entry/src/main/ets/components/ImagePreview.ets
import { PhotoPicker } from '../common/utils/PhotoPicker';
@Component
export struct ImagePreview {
@Prop imageUri: string = '';
@State showPicker: boolean = false;
build() {
Column() {
if (this.imageUri) {
Image(this.imageUri)
.width('100%')
.height(200)
.objectFit(ImageFit.Cover)
.onClick(() => {
// 点击图片预览大图
})
} else {
Button('选择图片')
.onClick(async () => {
const uri = await PhotoPicker.selectSingleImage();
if (uri) {
this.imageUri = uri;
}
})
}
}
}
}
七、图片处理
7.1 图片压缩
// entry/src/main/ets/common/utils/ImageProcessor.ts
import image from '@kit.ImageKit';
import fileIo from '@kit.CoreFileKit';
import { BusinessError } from '@ohos.base';
export class ImageProcessor {
// 压缩图片
static async compressImage(
imageUri: string,
maxWidth: number = 1080,
maxHeight: number = 1080,
quality: number = 80
): Promise<string | null> {
try {
// 打开图片文件
const file = fileIo.openSync(imageUri, fileIo.OpenMode.READ_ONLY);
// 创建图片源
const imageSource = image.createImageSource(file.fd);
if (!imageSource) {
throw new Error('创建图片源失败');
}
// 获取图片信息
const imageInfo = await imageSource.getImageInfo();
const { width, height } = imageInfo.size;
// 计算缩放比例
const scale = Math.min(maxWidth / width, maxHeight / height);
const targetWidth = Math.floor(width * scale);
const targetHeight = Math.floor(height * scale);
// 创建PixelMap
const pixelMap = await imageSource.createPixelMap({
desiredSize: { width: targetWidth, height: targetHeight }
});
// 压缩图片
const imagePacker = image.createImagePacker();
const compressedData = await imagePacker.packing(pixelMap, {
format: 'image/jpeg',
quality: quality
});
// 保存压缩后的图片
const compressedPath = imageUri.replace('.jpg', '_compressed.jpg');
const compressedFile = fileIo.openSync(compressedPath, fileIo.OpenMode.CREATE | fileIo.OpenMode.WRITE_ONLY);
await fileIo.write(compressedFile.fd, compressedData);
fileIo.closeSync(compressedFile.fd);
// 释放资源
pixelMap.release();
imagePacker.release();
fileIo.closeSync(file.fd);
return compressedPath;
} catch (error) {
console.error('压缩图片失败:', error);
return null;
}
}
// 裁剪图片
static async cropImage(
imageUri: string,
x: number,
y: number,
width: number,
height: number
): Promise<string | null> {
try {
const file = fileIo.openSync(imageUri, fileIo.OpenMode.READ_ONLY);
const imageSource = image.createImageSource(file.fd);
const pixelMap = await imageSource.createPixelMap();
// 裁剪图片
const croppedPixelMap = await pixelMap.crop({
x: x,
y: y,
width: width,
height: height
});
// 保存裁剪后的图片
const imagePacker = image.createImagePacker();
const imageData = await imagePacker.packing(croppedPixelMap, {
format: 'image/jpeg',
quality: 90
});
const croppedPath = imageUri.replace('.jpg', '_cropped.jpg');
const croppedFile = fileIo.openSync(croppedPath, fileIo.OpenMode.CREATE | fileIo.OpenMode.WRITE_ONLY);
await fileIo.write(croppedFile.fd, imageData);
fileIo.closeSync(croppedFile.fd);
// 释放资源
croppedPixelMap.release();
pixelMap.release();
imagePacker.release();
fileIo.closeSync(file.fd);
return croppedPath;
} catch (error) {
console.error('裁剪图片失败:', error);
return null;
}
}
// 旋转图片
static async rotateImage(imageUri: string, angle: number): Promise<string | null> {
try {
const file = fileIo.openSync(imageUri, fileIo.OpenMode.READ_ONLY);
const imageSource = image.createImageSource(file.fd);
const pixelMap = await imageSource.createPixelMap();
// 旋转图片
const rotatedPixelMap = await pixelMap.createRotated(angle);
// 保存旋转后的图片
const imagePacker = image.createImagePacker();
const imageData = await imagePacker.packing(rotatedPixelMap, {
format: 'image/jpeg',
quality: 90
});
const rotatedPath = imageUri.replace('.jpg', '_rotated.jpg');
const rotatedFile = fileIo.openSync(rotatedPath, fileIo.OpenMode.CREATE | fileIo.OpenMode.WRITE_ONLY);
await fileIo.write(rotatedFile.fd, imageData);
fileIo.closeSync(rotatedFile.fd);
// 释放资源
rotatedPixelMap.release();
pixelMap.release();
imagePacker.release();
fileIo.closeSync(file.fd);
return rotatedPath;
} catch (error) {
console.error('旋转图片失败:', error);
return null;
}
}
}
八、实战案例:多媒体应用
8.1 主页面实现
// entry/src/main/ets/pages/MainPage.ets
import { CameraManager } from '../common/utils/CameraManager';
import { AudioRecorder } from '../common/utils/AudioRecorder';
import { VideoRecorder } from '../common/utils/VideoRecorder';
import { PhotoPicker } from '../common/utils/PhotoPicker';
import { ImageProcessor } from '../common/utils/ImageProcessor';
@Entry
@Component
struct MainPage {
@State currentTab: string = 'camera';
@State cameraManager: CameraManager;
@State audioRecorder: AudioRecorder;
@State videoRecorder: VideoRecorder;
@State selectedImage: string = '';
@State isRecordingAudio: boolean = false;
@State isRecordingVideo: boolean = false;
aboutToAppear() {
const context = getContext(this) as common.UIAbilityContext;
this.cameraManager = new CameraManager(context);
this.audioRecorder = new AudioRecorder();
this.videoRecorder = new VideoRecorder();
}
// 拍照
async takePhoto() {
const pixelMap = await this.cameraManager.takePhoto();
if (pixelMap) {
const fileName = 'photo_' + Date.now() + '.jpg';
const saved = await this.cameraManager.savePhotoToGallery(pixelMap, fileName);
if (saved) {
promptAction.showToast({ message: '照片保存成功', duration: 2000 });
}
}
}
// 开始录音
async startAudioRecord() {
const context = getContext(this) as common.UIAbilityContext;
const started = await this.audioRecorder.startRecord(context);
if (started) {
this.isRecordingAudio = true;
}
}
// 停止录音
async stopAudioRecord() {
const filePath = await this.audioRecorder.stopRecord();
this.isRecordingAudio = false;
if (filePath) {
promptAction.showToast({ message: '录音已保存', duration: 2000 });
}
}
// 开始录像
async startVideoRecord() {
const context = getContext(this) as common.UIAbilityContext;
const started = await this.videoRecorder.startRecord(context);
if (started) {
this.isRecordingVideo = true;
}
}
// 停止录像
async stopVideoRecord() {
const filePath = await this.videoRecorder.stopRecord();
this.isRecordingVideo = false;
if (filePath) {
promptAction.showToast({ message: '录像已保存', duration: 2000 });
}
}
// 选择图片
async selectImage() {
const imageUri = await PhotoPicker.selectSingleImage();
if (imageUri) {
this.selectedImage = imageUri;
}
}
// 压缩图片
async compressImage() {
if (!this.selectedImage) {
promptAction.showToast({ message: '请先选择图片', duration: 2000 });
return;
}
const compressedPath = await ImageProcessor.compressImage(this.selectedImage);
if (compressedPath) {
promptAction.showToast({ message: '图片压缩成功', duration: 2000 });
this.selectedImage = compressedPath;
}
}
build() {
Column() {
// 标签栏
Tabs({
barPosition: BarPosition.Start,
index: this.getTabIndex(this.currentTab)
}) {
// 相机标签页
TabContent() {
this.buildCameraTab()
}.tabBar('相机')
// 录音标签页
TabContent() {
this.buildAudioTab()
}.tabBar('录音')
// 录像标签页
TabContent() {
this.buildVideoTab()
}.tabBar('录像')
// 图片标签页
TabContent() {
this.buildImageTab()
}.tabBar('图片')
}
}
}
// 相机标签页
@Builder
buildCameraTab() {
Column() {
Button('拍照')
.onClick(() => this.takePhoto())
.margin(20)
}
}
// 录音标签页
@Builder
buildAudioTab() {
Column() {
Button(this.isRecordingAudio ? '停止录音' : '开始录音')
.onClick(() => {
if (this.isRecordingAudio) {
this.stopAudioRecord();
} else {
this.startAudioRecord();
}
})
.margin(20)
}
}
// 录像标签页
@Builder
buildVideoTab() {
Column() {
Button(this.isRecordingVideo ? '停止录像' : '开始录像')
.onClick(() => {
if (this.isRecordingVideo) {
this.stopVideoRecord();
} else {
this.startVideoRecord();
}
})
.margin(20)
}
}
// 图片标签页
@Builder
buildImageTab() {
Column() {
Button('选择图片')
.onClick(() => this.selectImage())
.margin(20)
if (this.selectedImage) {
Image(this.selectedImage)
.width(200)
.height(200)
.margin(20)
Button('压缩图片')
.onClick(() => this.compressImage())
.margin(10)
}
}
}
// 获取标签索引
private getTabIndex(tab: string): number {
const tabs = ['camera', 'audio', 'video', 'image'];
return tabs.indexOf(tab);
}
}
九、最佳实践
9.1 权限管理优化
// entry/src/main/ets/common/utils/PermissionManager.ts
import abilityAccessCtrl from '@kit.AbilityKit';
import { BusinessError } from '@ohos.base';
export class PermissionManager {
// 检查并申请权限
static async checkAndRequestPermission(
context: common.UIAbilityContext,
permission: string
): Promise<boolean> {
try {
const atManager = abilityAccessCtrl.createAtManager();
// 检查权限状态
const tokenId = context.applicationInfo.accessTokenId;
const grantStatus = await atManager.checkAccessToken(tokenId, permission);
if (grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
return true;
}
// 申请权限
const result = await atManager.requestPermissionsFromUser(context, [permission]);
return result.authResults[0] === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED;
} catch (error) {
console.error('权限申请失败:', error);
return false;
}
}
// 批量申请权限
static async requestMultiplePermissions(
context: common.UIAbilityContext,
permissions: string[]
): Promise<boolean> {
try {
const atManager = abilityAccessCtrl.createAtManager();
const result = await atManager.requestPermissionsFromUser(context, permissions);
return result.authResults.every(
status => status === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED
);
} catch (error) {
console.error('批量权限申请失败:', error);
return false;
}
}
}
9.2 内存管理
// entry/src/main/ets/common/utils/MemoryManager.ts
export class MemoryManager {
// 检查内存使用情况
static checkMemoryUsage(): void {
const memoryInfo = system.getMemoryInfo();
console.log('内存使用情况:', {
total: memoryInfo.total,
available: memoryInfo.available,
used: memoryInfo.used,
threshold: memoryInfo.threshold
});
// 如果内存不足,释放非关键资源
if (memoryInfo.available < memoryInfo.threshold) {
this.releaseNonCriticalResources();
}
}
// 释放非关键资源
private static releaseNonCriticalResources(): void {
// 释放缓存图片、临时文件等
console.log('释放非关键资源');
}
}
9.3 错误处理
// entry/src/main/ets/common/utils/ErrorHandler.ts
import { BusinessError } from '@ohos.base';
export class ErrorHandler {
// 处理多媒体错误
static handleMediaError(error: BusinessError): void {
console.error('多媒体操作失败:', error);
switch (error.code) {
case 13900001:
console.error('权限不足,请检查权限设置');
break;
case 13900002:
console.error('参数错误,请检查参数配置');
break;
case 13900003:
console.error('资源不足,请释放资源后重试');
break;
case 13900004:
console.error('操作超时,请检查网络连接');
break;
default:
console.error('未知错误:', error.message);
}
}
}
十、总结与行动建议
10.1 核心要点回顾
- 相机开发:掌握相机初始化、预览、拍照、录像等核心功能
- 音视频录制:学会使用AVRecorder进行音频和视频录制
- 音视频播放:掌握AVPlayer的播放控制和状态管理
- 图片处理:了解图片选择、压缩、裁剪、旋转等操作
- 权限管理:正确处理多媒体权限申请和用户拒绝场景
10.2 性能优化建议
- 内存管理:及时释放PixelMap、AVPlayer等资源,避免内存泄漏
- 大文件处理:对大图片和视频进行压缩和缩放,减少内存占用
- 权限缓存:缓存权限状态,避免重复申请
- 错误重试:对网络错误和权限错误进行重试机制
10.3 下一步行动
- 实践练习:基于本篇文章的代码示例,实现一个完整的多媒体应用
- 功能扩展:添加滤镜效果、视频剪辑、图片编辑等高级功能
- 性能测试:在不同设备上测试多媒体功能的性能表现
- 用户体验优化:优化权限申请流程和错误提示,提升用户体验
通过本篇文章的学习,你已经掌握了HarmonyOS多媒体处理的核心能力。在实际开发中,建议参考官方文档和最佳实践,确保应用的稳定性和性能表现。
1950

被折叠的 条评论
为什么被折叠?



