一、HarmonyOS多媒体框架概述
HarmonyOS多媒体框架提供了统一的接口来访问设备的多媒体能力,支持跨设备协同工作。框架采用分层架构,从底层的硬件抽象到上层的应用接口,为开发者提供完整的多媒体解决方案。
1.1 多媒体核心组件
相机服务架构:
- CameraKit:相机能力集,提供拍照、录像、预览等核心功能
- ImageSource:图像数据源,支持多种格式的图像编解码
- EffectKit:实时特效处理,支持滤镜、美颜等效果
音频服务架构:
- AudioRenderer:音频渲染器,负责音频数据播放
- AudioCapturer:音频采集器,负责音频数据录制
- AudioStreamManager:音频流管理,支持多路音频混合
二、自定义相机实战
2.1 相机权限与配置
首先在module.json5
中声明必要的权限:
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.CAMERA",
"reason": "用于自定义相机拍照和录像功能",
"usedScene": {
"abilities": ["CameraAbility"],
"when": "inuse"
}
},
{
"name": "ohos.permission.MICROPHONE",
"reason": "录制视频时需要采集音频",
"usedScene": {
"abilities": ["CameraAbility"],
"when": "inuse"
}
},
{
"name": "ohos.permission.WRITE_MEDIA",
"reason": "保存拍摄的照片和视频到相册",
"usedScene": {
"abilities": ["CameraAbility"],
"when": "inuse"
}
}
]
}
}
2.2 相机初始化与预览
实现完整的相机管理类,处理相机的生命周期和状态管理:
import camera from '@ohos.multimedia.camera';
import image from '@ohos.multimedia.image';
import photoAccessHelper from '@ohos.file.photoAccessHelper';
@Component
export class CustomCameraManager {
private cameraManager: camera.CameraManager | null = null;
private cameraDevice: camera.CameraDevice | null = null;
private captureSession: camera.CaptureSession | null = null;
private previewOutput: camera.PreviewOutput | null = null;
private photoOutput: camera.PhotoOutput | null = null;
private videoOutput: camera.VideoOutput | null = null;
private isCameraReady: boolean = false;
// 初始化相机系统
async initializeCamera(context: common.Context): Promise<void> {
try {
this.cameraManager = camera.getCameraManager(context);
// 获取相机列表并选择后置相机
const cameras = this.cameraManager.getSupportedCameras();
const backCamera = cameras.find(cam =>
cam.cameraPosition === camera.CameraPosition.CAMERA_POSITION_BACK
);
if (!backCamera) {
throw new Error('未找到后置摄像头');
}
// 创建相机设备
this.cameraDevice = this.cameraManager.createCameraDevice(backCamera);
// 创建捕获会话
this.captureSession = this.cameraManager.createCaptureSession();
// 配置会话模板
await this.captureSession.beginConfig();
// 创建预览输出
this.previewOutput = this.cameraManager.createPreviewOutput(
this.getPreviewSurface()
);
await this.captureSession.addOutput(this.previewOutput);
// 创建照片输出
this.photoOutput = this.cameraManager.createPhotoOutput(
this.getPhotoProfile()
);
await this.captureSession.addOutput(this.photoOutput);
// 创建视频输出(如果需要录像)
this.videoOutput = this.cameraManager.createVideoOutput(
this.getVideoProfile()
);
await this.captureSession.addOutput(this.videoOutput);
// 提交配置并启动预览
await this.captureSession.commitConfig();
await this.captureSession.start();
this.isCameraReady = true;
console.info('相机初始化成功');
} catch (error) {
console.error('相机初始化失败:', error);
throw error;
}
}
// 拍照功能
async takePhoto(): Promise<string> {
if (!this.captureSession || !this.photoOutput) {
throw new Error('相机未就绪');
}
try {
const photo = await this.photoOutput.capture();
// 保存照片到相册
const photoUri = await this.savePhotoToGallery(photo);
console.info('照片保存成功:', photoUri);
return photoUri;
} catch (error) {
console.error('拍照失败:', error);
throw error;
}
}
// 实时滤镜处理
async applyRealTimeFilter(filterType: string): Promise<void> {
if (!this.captureSession) return;
try {
// 使用EffectKit应用实时滤镜
const effectKit = await import('@ohos.multimedia.effectKit');
const filter = effectKit.createEffect(filterType);
// 配置滤镜参数
await filter.configure({
intensity: 0.8,
colorAdjustment: this.getFilterParams(filterType)
});
// 将滤镜应用到预览流
await this.previewOutput?.addEffect(filter);
} catch (error) {
console.error('滤镜应用失败:', error);
}
}
// 手动相机控制
async setManualCameraSettings(settings: CameraSettings): Promise<void> {
if (!this.cameraDevice) return;
try {
// 设置曝光补偿
if (settings.exposureCompensation !== undefined) {
await this.cameraDevice.setExposureCompensation(settings.exposureCompensation);
}
// 设置ISO
if (settings.iso !== undefined) {
await this.cameraDevice.setIso(settings.iso);
}
// 设置快门速度
if (settings.shutterSpeed !== undefined) {
await this.cameraDevice.setShutterSpeed(settings.shutterSpeed);
}
// 设置对焦模式
if (settings.focusMode !== undefined) {
await this.cameraDevice.setFocusMode(settings.focusMode);
}
} catch (error) {
console.error('相机设置失败:', error);
}
}
}
2.3 相机UI界面实现
创建功能完整的相机用户界面:
@Entry
@Component
struct CustomCameraPage {
@State currentFilter: string = 'normal';
@State isRecording: boolean = false;
@State flashMode: string = 'off';
@State cameraMode: string = 'photo';
@State previewRatio: number = 16 / 9;
private cameraManager: CustomCameraManager = new CustomCameraManager();
private cameraContext: common.Context | null = null;
build() {
Column({ space: 0 }) {
// 相机预览区域
Stack({ alignContent: Alignment.Top }) {
// 相机预览Surface
CameraPreviewSurface({ manager: this.cameraManager })
.width('100%')
.height('80%')
.backgroundColor('#000000')
// 顶部控制栏
Row({ space: 20 }) {
Button('关闭')
.fontSize(16)
.fontColor('#FFFFFF')
.backgroundColor('rgba(0,0,0,0.5)')
.onClick(() => this.exitCamera())
Text(this.cameraMode === 'photo' ? '照片' : '视频')
.fontSize(18)
.fontColor('#FFFFFF')
.fontWeight(FontWeight.Bold)
Button(this.flashMode === 'on' ? '闪光灯开' : '闪光灯关')
.fontSize(16)
.fontColor('#FFFFFF')
.backgroundColor('rgba(0,0,0,0.5)')
.onClick(() => this.toggleFlash())
}
.width('100%')
.padding(20)
.justifyContent(FlexAlign.SpaceBetween)
}
.layoutWeight(1)
// 底部控制区域
Column({ space: 15 }) {
// 模式选择
Row({ space: 30 }) {
Button('照片')
.selected(this.cameraMode === 'photo')
.onClick(() => this.switchMode('photo'))
Button('视频')
.selected(this.cameraMode === 'video')
.onClick(() => this.switchMode('video'))
}
// 拍摄按钮
Row({ space: 40 }) {
// 滤镜选择
Scroll() {
Row({ space: 10 }) {
ForEach(this.getAvailableFilters(), (filter: string) => {
Text(this.getFilterName(filter))
.fontSize(14)
.fontColor(this.currentFilter === filter ? '#007AFF' : '#FFFFFF')
.padding(8)
.borderRadius(20)
.backgroundColor(this.currentFilter === filter ?
'rgba(0,122,255,0.2)' : 'rgba(255,255,255,0.1)')
.onClick(() => this.applyFilter(filter))
})
}
}
.scrollable(ScrollDirection.Horizontal)
// 拍摄按钮
Circle({ width: 70, height: 70 })
.fill(this.isRecording ? '#FF3B30' : '#FFFFFF')
.onClick(() => this.captureAction())
.margin({ left: 20, right: 20 })
// 相册入口
Button('相册')
.fontSize(16)
.fontColor('#FFFFFF')
}
.justifyContent(FlexAlign.SpaceAround)
}
.padding(20)
.backgroundColor('rgba(0,0,0,0.8)')
.height('20%')
}
.width('100%')
.height('100%')
.backgroundColor('#000000')
}
aboutToAppear(): void {
this.cameraContext = getContext(this) as common.Context;
this.initializeCamera();
}
async initializeCamera(): Promise<void> {
try {
await this.cameraManager.initializeCamera(this.cameraContext!);
} catch (error) {
console.error('相机初始化失败:', error);
}
}
async captureAction(): Promise<void> {
if (this.cameraMode === 'photo') {
await this.takePhoto();
} else {
await this.toggleRecording();
}
}
async takePhoto(): Promise<void> {
try {
const photoUri = await this.cameraManager.takePhoto();
// 显示拍摄结果
this.showCaptureResult(photoUri);
} catch (error) {
console.error('拍照失败:', error);
}
}
}
三、音频播放器实战
3.1 音频播放器核心实现
创建功能完整的音频播放器,支持多种音频格式和播放模式:
import audio from '@ohos.multimedia.audio';
import mediaLibrary from '@ohos.file.mediaLibrary';
@Component
export class AudioPlayerManager {
private audioRenderer: audio.AudioRenderer | null = null;
private audioStreamInfo: audio.AudioStreamInfo | null = null;
private currentState: PlayerState = PlayerState.IDLE;
private currentPlaylist: AudioItem[] = [];
private currentIndex: number = 0;
private playbackSpeed: number = 1.0;
private equalizer: audio.AudioEffect | null = null;
// 初始化音频渲染器
async initializeAudioRenderer(options: AudioRendererOptions): Promise<void> {
try {
this.audioStreamInfo = {
samplingRate: options.samplingRate || audio.AudioSamplingRate.SAMPLE_RATE_44100,
channels: options.channels || audio.AudioChannel.CHANNEL_2,
sampleFormat: options.sampleFormat || audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE,
encodingType: options.encodingType || audio.AudioEncodingType.ENCODING_TYPE_RAW
};
const audioRendererOptions: audio.AudioRendererOptions = {
streamInfo: this.audioStreamInfo,
rendererInfo: {
content: audio.ContentType.CONTENT_TYPE_MUSIC,
usage: audio.StreamUsage.STREAM_USAGE_MEDIA,
rendererFlags: 0
}
};
this.audioRenderer = await audio.createAudioRenderer(audioRendererOptions);
await this.audioRenderer.start();
this.currentState = PlayerState.READY;
console.info('音频播放器初始化成功');
} catch (error) {
console.error('音频播放器初始化失败:', error);
throw error;
}
}
// 播放音频
async playAudio(audioItem: AudioItem): Promise<void> {
if (!this.audioRenderer) {
throw new Error('音频播放器未初始化');
}
try {
// 停止当前播放
if (this.currentState === PlayerState.PLAYING) {
await this.stop();
}
// 加载音频数据
const audioData = await this.loadAudioData(audioItem.uri);
// 配置音频效果
await this.configureAudioEffects(audioItem);
// 开始播放
await this.audioRenderer.write(audioData);
this.currentState = PlayerState.PLAYING;
// 设置播放完成回调
this.audioRenderer.on('endOfStream', () => {
this.handlePlaybackComplete();
});
} catch (error) {
console.error('音频播放失败:', error);
throw error;
}
}
// 音频效果处理
async configureAudioEffects(audioItem: AudioItem): Promise<void> {
if (!this.audioRenderer) return;
try {
// 创建均衡器
this.equalizer = await audio.createAudioEffect(audio.AudioEffectType.EQUALIZER);
// 配置音效预设
const preset = this.getEqualizerPreset(audioItem.genre);
await this.equalizer.setParameter({
bandLevels: preset.bandLevels,
preset: preset.presetType
});
// 应用音效到音频渲染器
await this.audioRenderer.attachAudioEffect(this.equalizer);
} catch (error) {
console.error('音效配置失败:', error);
}
}
// 变速播放
async setPlaybackSpeed(speed: number): Promise<void> {
if (!this.audioRenderer || speed < 0.5 || speed > 2.0) {
return;
}
try {
this.playbackSpeed = speed;
await this.audioRenderer.setPlaybackSpeed(speed);
} catch (error) {
console.error('设置播放速度失败:', error);
}
}
// 睡眠定时器
async setSleepTimer(minutes: number): Promise<void> {
setTimeout(async () => {
if (this.currentState === PlayerState.PLAYING) {
await this.pause();
console.info('睡眠定时器:播放已停止');
}
}, minutes * 60 * 1000);
}
}
3.2 音频播放器UI界面
创建美观易用的音频播放界面:
@Entry
@Component
struct AudioPlayerPage {
@State currentSong: AudioItem | null = null;
@State isPlaying: boolean = false;
@State currentTime: number = 0;
@State totalTime: number = 0;
@State playbackRate: number = 1.0;
@State equalizerPreset: string = 'normal';
@State showPlaylist: boolean = false;
private audioManager: AudioPlayerManager = new AudioPlayerManager();
private progressTimer: number = 0;
build() {
Column({ space: 0 }) {
// 专辑封面和歌曲信息
Column({ space: 20 }) {
Image(this.currentSong?.coverUri || $r('app.media.default_cover'))
.width(280)
.height(280)
.borderRadius(20)
.objectFit(ImageFit.Contain)
Column({ space: 10 }) {
Text(this.currentSong?.title || '未选择歌曲')
.fontSize(24)
.fontColor('#FFFFFF')
.fontWeight(FontWeight.Bold)
Text(this.currentSong?.artist || '未知艺术家')
.fontSize(16)
.fontColor('#CCCCCC')
}
}
.layoutWeight(1)
.padding(40)
// 播放进度条
Column({ space: 10 }) {
Slider({
value: this.currentTime,
min: 0,
max: this.totalTime,
step: 1,
style: SliderStyle.OutSet
})
.width('90%')
.onChange((value: number) => {
this.seekTo(value);
})
Row({ space: 0 }) {
Text(this.formatTime(this.currentTime))
.fontSize(12)
.fontColor('#CCCCCC')
.layoutWeight(1)
.textAlign(TextAlign.Start)
Text(this.formatTime(this.totalTime))
.fontSize(12)
.fontColor('#CCCCCC')
.layoutWeight(1)
.textAlign(TextAlign.End)
}
.width('90%')
}
.padding(20)
// 播放控制区域
Column({ space: 25 }) {
// 音效控制
Row({ space: 15 }) {
Button('变速')
.fontSize(14)
.fontColor(this.playbackRate !== 1.0 ? '#007AFF' : '#FFFFFF')
.onClick(() => this.showSpeedOptions())
Button('音效')
.fontSize(14)
.fontColor(this.equalizerPreset !== 'normal' ? '#007AFF' : '#FFFFFF')
.onClick(() => this.showEqualizerOptions())
Button('定时')
.fontSize(14)
.fontColor('#FFFFFF')
.onClick(() => this.showSleepTimer())
}
// 主要控制按钮
Row({ space: 40 }) {
Button('上一首')
.fontSize(16)
.onClick(() => this.previousSong())
Button(this.isPlaying ? '暂停' : '播放')
.fontSize(20)
.fontColor('#000000')
.backgroundColor('#FFFFFF')
.borderRadius(30)
.width(60)
.height(60)
.onClick(() => this.togglePlayback())
Button('下一首')
.fontSize(16)
.onClick(() => this.nextSong())
}
// 音量和其他控制
Row({ space: 30 }) {
Button('播放列表')
.fontSize(14)
.onClick(() => this.togglePlaylist())
Button('随机播放')
.fontSize(14)
.onClick(() => this.toggleShuffle())
Button('循环模式')
.fontSize(14)
.onClick(() => this.toggleLoopMode())
}
}
.padding(30)
.backgroundColor('rgba(0,0,0,0.8)')
}
.width('100%')
.height('100%')
.backgroundColor('#000000')
}
}
四、多设备音频接力实战
4.1 分布式音频管理
实现跨设备音频接力功能,让用户在不同设备间无缝切换音频播放:
import distributedAudio from '@ohos.multimedia.distributedAudio';
import deviceManager from '@ohos.distributedDeviceManager';
@Component
export class DistributedAudioManager {
private audioDeviceManager: distributedAudio.AudioDeviceManager | null = null;
private currentDeviceId: string = '';
private availableDevices: distributedAudio.AudioDevice[] = [];
// 初始化分布式音频
async initializeDistributedAudio(): Promise<void> {
try {
this.audioDeviceManager = distributedAudio.createAudioDeviceManager();
// 监听设备变化
this.audioDeviceManager.on('deviceChange', (devices) => {
this.handleDeviceChange(devices);
});
// 发现可用音频设备
await this.discoverAudioDevices();
} catch (error) {
console.error('分布式音频初始化失败:', error);
}
}
// 发现可用音频设备
async discoverAudioDevices(): Promise<void> {
if (!this.audioDeviceManager) return;
try {
const devices = await this.audioDeviceManager.getAvailableDevices();
this.availableDevices = devices.filter(device =>
device.deviceType !== distributedAudio.DeviceType.UNKNOWN
);
console.info(`发现${this.availableDevices.length}个可用音频设备`);
} catch (error) {
console.error('设备发现失败:', error);
}
}
// 切换到其他设备播放
async switchPlaybackDevice(deviceId: string, audioItem: AudioItem): Promise<void> {
if (!this.audioDeviceManager) {
throw new Error('分布式音频未初始化');
}
try {
// 暂停当前设备播放
await this.pauseCurrentPlayback();
// 切换到目标设备
await this.audioDeviceManager.switchOutputDevice(deviceId);
// 在新设备上恢复播放
await this.resumePlaybackOnDevice(deviceId, audioItem);
this.currentDeviceId = deviceId;
console.info(`音频已切换到设备: ${deviceId}`);
} catch (error) {
console.error('设备切换失败:', error);
throw error;
}
}
// 多设备同步播放
async startMultiDevicePlayback(devices: string[], audioItem: AudioItem): Promise<void> {
if (!this.audioDeviceManager) return;
try {
// 创建同步播放组
const syncGroup = await this.audioDeviceManager.createSyncGroup(devices);
// 配置同步参数
await syncGroup.configure({
syncTolerance: 50, // 50ms同步容差
masterDevice: devices[0] // 主设备
});
// 在所有设备上开始同步播放
await Promise.all(
devices.map(deviceId =>
this.startPlaybackOnDevice(deviceId, audioItem)
)
);
console.info(`在${devices.length}个设备上开始同步播放`);
} catch (error) {
console.error('多设备同步播放失败:', error);
}
}
}
4.2 设备选择界面
创建设备选择界面,让用户可以轻松管理多设备音频:
@Component
struct DeviceSelectorDialog {
@Link selectedDevice: string;
@Link availableDevices: distributedAudio.AudioDevice[];
build() {
Column({ space: 20 }) {
Text('选择播放设备')
.fontSize(20)
.fontColor('#000000')
.fontWeight(FontWeight.Bold)
List({ space: 10 }) {
ForEach(this.availableDevices, (device: distributedAudio.AudioDevice) => {
ListItem() {
Row({ space: 15 }) {
Image(this.getDeviceIcon(device.deviceType))
.width(30)
.height(30)
Column({ space: 5 }) {
Text(device.deviceName)
.fontSize(16)
.fontColor('#000000')
.textAlign(TextAlign.Start)
Text(this.getDeviceStatus(device))
.fontSize(12)
.fontColor('#666666')
}
.layoutWeight(1)
if (device.deviceId === this.selectedDevice) {
Image($r('app.media.ic_selected'))
.width(20)
.height(20)
}
}
.padding(15)
.backgroundColor(device.deviceId === this.selectedDevice ?
'#E6F2FF' : '#FFFFFF')
.borderRadius(10)
}
.onClick(() => {
this.selectedDevice = device.deviceId;
})
})
}
.layoutWeight(1)
Button('确认切换')
.width('80%')
.height(40)
.fontSize(16)
.onClick(() => {
// 处理设备切换
this.confirmSelection();
})
}
.padding(20)
.backgroundColor('#FFFFFF')
.borderRadius(20)
}
}
五、性能优化与最佳实践
5.1 内存管理优化
@Component
export class MediaMemoryManager {
private static readonly MAX_CACHE_SIZE = 100 * 1024 * 1024; // 100MB
private currentCacheSize: number = 0;
private mediaCache: Map<string, ArrayBuffer> = new Map();
// 智能缓存管理
async cacheMediaData(mediaUri: string, data: ArrayBuffer): Promise<void> {
const dataSize = data.byteLength;
// 检查缓存限制
if (this.currentCacheSize + dataSize > MediaMemoryManager.MAX_CACHE_SIZE) {
await this.cleanupCache();
}
this.mediaCache.set(mediaUri, data);
this.currentCacheSize += dataSize;
}
// 内存压力处理
@WatchSystemMemory
async onMemoryPressure(level: MemoryPressureLevel): Promise<void> {
switch (level) {
case MemoryPressureLevel.CRITICAL:
await this.clearAllCache();
break;
case MemoryPressureLevel.HIGH:
await this.cleanupCache(0.5); // 清理50%缓存
break;
case MemoryPressureLevel.MEDIUM:
await this.cleanupCache(0.3); // 清理30%缓存
break;
}
}
// 媒体资源预加载
async preloadMediaResources(resources: string[]): Promise<void> {
const preloadPromises = resources.map(async resource => {
if (!this.mediaCache.has(resource)) {
const data = await this.loadMediaData(resource);
await this.cacheMediaData(resource, data);
}
});
await Promise.all(preloadPromises);
}
}
总结
本文通过完整的实战案例展示了HarmonyOS多媒体开发的核心技术,包括自定义相机实现、音频播放器开发以及多设备音频接力功能。关键要点包括:
- 相机开发:掌握相机API的完整使用流程,包括权限管理、预览配置、拍照录像和实时特效处理
- 音频处理:实现高质量的音频播放器,支持音效处理、变速播放和睡眠定时等高级功能
- 分布式能力:利用HarmonyOS的分布式特性实现多设备音频接力,提供无缝的用户体验
- 性能优化:通过内存管理和资源预加载确保多媒体应用的流畅运行