From 09df14ce2415a9cde1620ab0e6998953a625813c Mon Sep 17 00:00:00 2001 From: hao-xiansheng Date: Tue, 11 Oct 2022 14:29:09 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A7=86=E9=A2=91=E6=92=AD=E6=94=BE=E9=80=BB?= =?UTF-8?q?=E8=BE=91=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/ets/Model/media/MediaAssetBuilder.ts | 112 +++++++ .../main/ets/Model/media/MediaConstants.ts | 252 +++++++++++++++ .../main/ets/Model/media/MediaLibOperator.ts | 145 +++++++++ .../main/ets/Model/media/MediaPlayService.ts | 292 ++++++++++++++++++ 4 files changed, 801 insertions(+) create mode 100644 entry/src/main/ets/Model/media/MediaAssetBuilder.ts create mode 100644 entry/src/main/ets/Model/media/MediaConstants.ts create mode 100644 entry/src/main/ets/Model/media/MediaLibOperator.ts create mode 100644 entry/src/main/ets/Model/media/MediaPlayService.ts diff --git a/entry/src/main/ets/Model/media/MediaAssetBuilder.ts b/entry/src/main/ets/Model/media/MediaAssetBuilder.ts new file mode 100644 index 0000000..237b6bf --- /dev/null +++ b/entry/src/main/ets/Model/media/MediaAssetBuilder.ts @@ -0,0 +1,112 @@ +import mediaLibrary from '@ohos.multimedia.mediaLibrary'; + +import fileIO from '@ohos.fileio'; +import MediaLibOperator from './MediaLibOperator'; +import { MediaAsset, MediaOperationAsset, MediaSourceType } from './MediaConstants'; +import data_dataShare from '@ohos.data.dataShare'; +/* + *音视频播放服务类 + */ +export default class MediaAssetBuilder { + private mediaLibOperator: MediaLibOperator + private mediaLibAsset: MediaOperationAsset + private mediaAsset: MediaAsset + private realUrl + private sourceType: MediaSourceType = MediaSourceType.DEFAULT + private fileDescription: number + + constructor() { + this.mediaLibOperator = new MediaLibOperator() + } + + public async build(asset: MediaAsset) { + this.mediaAsset = asset + let originSource = this.mediaAsset.getSource() + console.log('hans ----->><<<<>'+originSource) + + if (!isNaN(originSource)) { + //Src为数字=fileId + this.mediaLibAsset = await this.mediaLibOperator.openMediaFileOperationById(originSource) + this.fileDescription = this.mediaLibAsset.getFd() + this.sourceType = MediaSourceType.MEDIA_LIB_FILE + this.mediaAsset.setTitle(this.mediaLibAsset.getAsset().displayName) + this.realUrl = "fd://" + this.mediaLibAsset.getFd() + } else if (originSource.startsWith('data/') || originSource.startsWith('/data')) { + let fd = fileIO.openSync(originSource) + this.fileDescription = fd + this.sourceType = MediaSourceType.ABSOLUTE_PATH_FILE + this.realUrl = "fd://" + fd + } else if (originSource.indexOf('../resources/rawfile/') >= 0) { + let rawfileFd = await globalThis.context.resourceManager.getRawFileDescriptor(originSource) + this.fileDescription = rawfileFd.fd + this.sourceType = MediaSourceType.RAWFILE_FILE + this.realUrl = "fd://" + rawfileFd.fd + } else if (originSource.indexOf('dataability:') >= 0) { + // let rawfileFd = await globalThis.context.resourceManager.getRawFileDescriptor(originSource) + + let list:any = await this.getAssetList(globalThis.appS.Get('isAudio') == true ?mediaLibrary.MediaType.AUDIO:mediaLibrary.MediaType.VIDEO) + console.log('hans ----->><<<<>222') + for (var index = 0; index < list.length; index++) { + const element = list[index]; + if (element.uri == originSource) { + this.mediaLibAsset = await this.mediaLibOperator.openMediaFileOperationById(element.id) + this.fileDescription = this.mediaLibAsset.getFd() + this.sourceType = MediaSourceType.MEDIA_LIB_FILE + this.mediaAsset.setTitle(this.mediaLibAsset.getAsset().displayName) + this.realUrl = "fd://" + this.mediaLibAsset.getFd() + break; + } + } + } else { + this.realUrl = originSource + } + return this.mediaAsset + } + + public getRealUrl() { + return this.realUrl + } + //获取公共资源 + async getAssetList(MediaType){ + let imageType = MediaType; + let fileKeyObj = await mediaLibrary.FileKey //文件关键信息 + let getImageOp = { + selections: fileKeyObj.MEDIA_TYPE + '=?', + selectionArgs: [imageType.toString()], + order: fileKeyObj.DATE_ADDED + " DESC", + extendArgs: "", + isEditable:true + }; + const fetchFileResult = await mediaLibrary.getMediaLibrary(globalThis.context).getFileAssets(getImageOp); + + var assetList = [] + await fetchFileResult.getAllObject().then((data)=>{ + for(var i = 0;i { + let publicPath = await this.sysMediaLib.getPublicDirectory(this.getSaveDir(mediaType)); + let mediaBuilderAsset = await this.sysMediaLib.createAsset(this.getSysMediaType(mediaType), creationName, publicPath) + let fd = await this.openMediaFileOperation(mediaBuilderAsset, 'rw') + if (fd > 0) { + let operationAsset = new MediaOperationAsset() + operationAsset.setAsset(mediaBuilderAsset) + operationAsset.setFd(fd) + return operationAsset + } + } + + /* + *获取媒资操作符操作权限 + * + *return fd + */ + async openMediaFileOperation(mediaFileAsset, operation): Promise { + if (mediaFileAsset != null) { + let operationAuth = await mediaFileAsset.open(operation); + if (operationAuth > 0) { + return operationAuth + } + } + } + + async closeOMediaFileOperation(mediaFileAsset, fd): Promise { + if (mediaFileAsset != null) { + mediaFileAsset.close(fd) + } + } + + /* + * 打开指定媒资操作通道 + * + *return fileAsset and fd + */ + async openMediaFileOperationById(fileId): Promise { + let fileKeyObj = mediaLibrary.FileKey; + let fetchOp = { + selections: fileKeyObj.ID + '= ?', + selectionArgs: [fileId.toString()], + order: fileKeyObj.DATE_ADDED + " DESC", + } + let fetchFileResult = await this.sysMediaLib.getFileAssets(fetchOp); + let fileAsset = await fetchFileResult.getFirstObject(); + let fd = await this.openMediaFileOperation(fileAsset, 'r') + if (fd > 0) { + let operationAsset = new MediaOperationAsset() + operationAsset.setAsset(fileAsset) + operationAsset.setFd(fd) + return operationAsset + } + } + + async getAsset(mediaType, fileId) { + let fileKeyObj = mediaLibrary.FileKey; + let fetchOp = { + selections: fileKeyObj.ID + '= ?', + selectionArgs: [fileId.toString()], + }; + let fetchFileResult = await this.sysMediaLib.getFileAssets(fetchOp); + let fileAsset = await fetchFileResult.getFirstObject(); + return fileAsset + } + + /* + *@param mediaType 非必需,不传时查全部类型 + */ + async getAllAssets(mediaTypes?: MediaType[]) { + if (mediaTypes == null) { + mediaTypes = [] + } + if (mediaTypes.length == 0) { + mediaTypes.push(MediaType.FILE) + mediaTypes.push(MediaType.IMAGE) + mediaTypes.push(MediaType.AUDIO) + mediaTypes.push(MediaType.VIDEO) + } + let selections = '' + let selectionArgs = [] + let fileKeyObj = mediaLibrary.FileKey; + for (let i = 0; i < mediaTypes.length; i++) { + selections += ((i == 0 ? '' : ' or ') + fileKeyObj.MEDIA_TYPE + '= ?') + selectionArgs.push(this.getSysMediaType(mediaTypes[i]).toString()) + } + let fetchOp = { + selections: selections, + selectionArgs: selectionArgs, + order: fileKeyObj.DATE_ADDED + " DESC", + }; + let fetchFileResult = await this.sysMediaLib.getFileAssets(fetchOp); + let fileAssets = await fetchFileResult.getAllObject(); + return fileAssets + } +} \ No newline at end of file diff --git a/entry/src/main/ets/Model/media/MediaPlayService.ts b/entry/src/main/ets/Model/media/MediaPlayService.ts new file mode 100644 index 0000000..be48527 --- /dev/null +++ b/entry/src/main/ets/Model/media/MediaPlayService.ts @@ -0,0 +1,292 @@ + +import MediaAssetBuilder from './MediaAssetBuilder' +import brightness from '@ohos.brightness'; +import { StatusChangedListener, MediaPlayerState, TAGS, MediaPlaySpeed, MediaAsset, MediaOperationAsset +} from './MediaConstants'; +import media from '@ohos.multimedia.media' +import LogUtils from '../../util/LogUtils' + +export default class MediaPlayService { + private mediaAssetBuilder: MediaAssetBuilder + private state = MediaPlayerState.IDLE + private mediaPlayer + private displaySurfaceId + private displayNeedChanged: boolean = false + private isPrepared: boolean + private volumeValue: number = 0.5 + private brightness: number = 0.5 + private statusChangedListeners: StatusChangedListener[] + private mediaAsset: MediaAsset + private mediaLibAsset: MediaOperationAsset + + constructor() { + this.mediaAssetBuilder = new MediaAssetBuilder() + this.statusChangedListeners = new Array() + this.initPlayer() + } + + private initPlayer() { + media.createVideoPlayer().then((player) => { + if (player != null) { + LogUtils.info(TAGS.MEDIA_PLAYER, 'createVideoPlayer success!'); + this.mediaPlayer = player + this.setCallback() + } + }, this.failureCallback).catch(this.catchCallback) + } + + /* + *play/seek/playbackCompleted回调BUFFERING_START和BUFFERING_END + */ + private setCallback() { + this.mediaPlayer.on('playbackCompleted', () => { + this.stepCallback(MediaPlayerState.FINISH, null) + }); + this.mediaPlayer.on('bufferingUpdate', (infoType, value) => { + + console.log('&&&-->value:'+value) + switch (infoType) { + case media.BufferingInfoType.BUFFERING_START: + this.stepCallback(MediaPlayerState.BUFFERING_START, value) + break; + case media.BufferingInfoType.BUFFERING_END: + this.stepCallback(MediaPlayerState.BUFFERING_END, value) + break; + case media.BufferingInfoType.BUFFERING_PERCENT: + this.stepCallback(MediaPlayerState.BUFFERING_PERCENT, value) + break; + case media.BufferingInfoType.CACHED_DURATION: + this.stepCallback(MediaPlayerState.CACHED_DURATION, value) + break; + } + if (this.getCurrentTime() == this.getDuration()) { + this.state = MediaPlayerState.FINISH + } else if (this.mediaPlayer.state == 'playing' || this.mediaPlayer.state == 'prepared') { + this.state = MediaPlayerState.PLAY + } else if (this.mediaPlayer.state == 'paused') { + this.state = MediaPlayerState.PAUSE + } else if (this.mediaPlayer.state == 'stopped') { + this.state = MediaPlayerState.STOP + } else if (this.mediaPlayer.state == 'error') { + this.state = MediaPlayerState.ERROR + } + }); + this.mediaPlayer.on('startRenderFrame', () => { + this.stepCallback(MediaPlayerState.START, null) + this.state = MediaPlayerState.PLAY + }); + this.mediaPlayer.on('videoSizeChanged', (width, height) => { + this.stepCallback(MediaPlayerState.SIZE_CHANGED, { + width: width, height: height + }) + }); + this.mediaPlayer.on('error', (error) => { + this.stepCallback(MediaPlayerState.ERROR, error) + }); + } + + private getKeyFrame(ms: number): number{ + console.log('&&&-->getKeyFrame:'+ms+',Source:'+Math.round(ms / 1000) * 1000) + return Math.round(ms / 1000) * 1000 + } + + public addStatusChangedListener(listener: StatusChangedListener) { + this.statusChangedListeners.push(listener) + } + + public addSurface(surfaceId) { + this.displaySurfaceId = surfaceId + this.displayNeedChanged = true + } + + /* + *@param asset.playSrc string|number + * 支持网络路径:http:// + * hls直播流:m3u8 + * 支持本地路径:data/ + * 支持媒体库fileId + * 支持项目路径../../resources/rawfile/ + */ + public async loadAsset(asset: MediaAsset, isAutoPlay: boolean, seekMs?: number) { + await this.stop() + if (this.mediaAsset == null || this.mediaAsset.getSource() != asset.getSource()) { + await this.reset() + this.mediaAsset = await this.mediaAssetBuilder.build(asset) + this.mediaPlayer.url = this.mediaAssetBuilder.getRealUrl() + this.stepCallback(MediaPlayerState.LOAD, { + asset: this.mediaAsset + }) + } + if (this.displayNeedChanged) { + await this.mediaPlayer.setDisplaySurface(this.displaySurfaceId) + this.displayNeedChanged = false + } + this.start(isAutoPlay, seekMs) + } + + private start(isAutoPlay: boolean, seekMs?: number) { + this.mediaPlayer.prepare().then(() => { + this.isPrepared = true + + console.log('&&&-->.getDuration():'+this.getDuration()) + this.stepCallback(MediaPlayerState.PREPARED, { + duration: this.getDuration() + }) + if (isAutoPlay) { + this.play(seekMs) + } + }).catch((error) => { + this.stepCallback(MediaPlayerState.ERROR, error) + }); + } + + public async play(seekMs?: number) { + if (!this.isPrepared) { + this.start(true, seekMs) + } else { + this.mediaPlayer.play().then(() => { + if (seekMs) { + this.seek(seekMs) + } + this.stepCallback(MediaPlayerState.PLAY, null) + }) + } + } + + public pause() { + if (this.isPrepared && this.state == MediaPlayerState.PLAY) { + this.mediaPlayer.pause().then(() => { + this.stepCallback(MediaPlayerState.PAUSE, null) + }); + } + } + + public resume() { + if (this.state == MediaPlayerState.PAUSE) { + this.play() + } + } + + public seek(ms) { + if (this.isPrepared && this.state != MediaPlayerState.ERROR) { + let seekMode = this.getCurrentTime() < ms ? 0 : 1 + let realTime = (ms <= 0 ? 0 : (ms >= this.getDuration() ? this.getDuration() : this.getKeyFrame(ms))) + this.mediaPlayer.seek(realTime, seekMode) + } + } + + public setSpeed(speed: MediaPlaySpeed) { + if (this.isPrepared) { + this.mediaPlayer.setSpeed(speed) + } + } + + /* + *@param vol [0.00-1.00] + */ + public async setVolume(vol) { + if (this.isPrepared) { + let value = (vol <= 0 ? 0 : (vol >= 1 ? 1 : vol)) + await this.mediaPlayer.setVolume(value) + this.volumeValue = value + } + } + + public getVolume() { + return this.volumeValue + } + + /* + *@param bright [0.00-1.00] bright设置为0时屏幕无亮度 + */ + public setBrightness(bright: number) { + let value = (bright <= 0 ? 0.1 : (bright >= 1 ? 1 : bright)) + console.log('&&&-->.setBrightness():'+value * 255) + brightness.setValue(value * 255) + this.brightness = bright + } + + public getBrightness() { + return this.brightness + } + + public resizeScreen(width: number, height: number) { + if (width >= 0 && height >= 0) { + this.stepCallback(MediaPlayerState.SIZE_CHANGED, { + width: width, height: height + }) + } + } + + public async stop() { + if (this.isPrepared) { + await this.mediaPlayer.stop() + this.stepCallback(MediaPlayerState.STOP, null) + } + } + + private async reset() { + await this.mediaAssetBuilder.release() + await this.mediaPlayer.reset() + this.isPrepared = false + } + + public release() { + this.stop() + this.reset() + this.mediaPlayer.release() + this.stepCallback(MediaPlayerState.IDLE, null) + this.statusChangedListeners.length = 0 + this.statusChangedListeners = null + } + + public getCurrentTime() { + if (this.isPrepared) { + return this.mediaPlayer.currentTime + } + return 0 + } + + public getDuration() { + if (this.isPrepared) { + return this.mediaPlayer.duration + } + return 0 + } + + async getTrackDescription() { + return await this.mediaPlayer.getTrackDescription() + } + + public getPlayerState() { + return this.state + } + + public getMediaAsset(): MediaAsset{ + return this.mediaAsset + } + + /* 函数调用发生状态改变 */ + private stepCallback(state, extra) { + console.log('&&&-->.stepCallback(), state:' + state + ',extra is ' + extra) + LogUtils.info(TAGS.MEDIA_PLAYER, 'stepCallback, state:' + state + ',extra is ' + extra); + this.state = state + for (let i = 0; i < this.statusChangedListeners.length; i++) { + this.statusChangedListeners[i](state, extra) + } + } + + failureCallback= (error) => { + LogUtils.info(TAGS.MEDIA_PLAYER, `error happened,error Name is ${error.name}`); + LogUtils.info(TAGS.MEDIA_PLAYER, `error happened,error Code is ${error.code}`); + LogUtils.info(TAGS.MEDIA_PLAYER, `error happened,error Message is ${error.message}`); + } + + // 当函数调用发生异常时用于上报错误信息 + catchCallback= (error) => { + LogUtils.info(TAGS.MEDIA_PLAYER, `catch error happened,error Name is ${error.name}`); + LogUtils.info(TAGS.MEDIA_PLAYER, `catch error happened,error Code is ${error.code}`); + LogUtils.info(TAGS.MEDIA_PLAYER, `catch error happened,error Message is ${error.message}`); + } +} + -- Gitee