网易首页 > 网易号 > 正文 申请入驻

鸿蒙应用开发:实现简单的媒体播放器

0
分享至

摘要:基于 Harmony OS 实现一款简单的媒体播放器。

文|即构引擎开发团队

距离 Harmony OS 发布已过去了一段时间,为了了解鸿蒙系统的功能与特性,今天我们将准备使用系统 API 实现一个简单的媒体播放器 demo。

大家在阅读本文后会对媒体播放器相关的 API 有一定的了解,并且可以根据文中的步骤一起动手实操,实现在鸿蒙系统上的简单媒体播放器!

VideoPlayerDemo 仓库地址先贴一下:https://gitee.com/wangx-six/video-player-demo

话不多说,下面我将带领大家一起看一下媒体播放器的实现流程。

媒体资源文件的权限申请

Harmony OS 中所有的应用均在应用沙盒内运行。默认情况下,应用只能访问有限的系统资源,系统负责管理应用对资源的访问权限。

所以要实现媒体播放器功能当然需要访问媒体资源,这时就需要向操作系统申请权限:

1、需要在 config.json 中的 ""reqPermissions" 添加配置:

"reqPermissions": ["name": "ohos.permission.READ_USER_STORAGE"

2、需要在程序启动时请求读取本地媒体资源的权限:

if (verifyCallingOrSelfPermission(SystemPermission.READ_USER_STORAGE) != IBundleManager.PERMISSION_GRANTED) {requestPermissionsFromUser(new String[]{SystemPermission.READ_USER_STORAGE}, REQUEST_CODE);

获取本地的媒体资源文件

在申请到媒体资源的权限后,我们就可以去获取本地的媒体资源信息,包括音频及视频。

我们可以通过以下操作,将所有的音视频文件标识为 AVElement 并存入 List 中方便我们做 UI 展示,稍后可通过 AVElement 获取媒体资源文件路径,传递给系统播放器。

private final List avElements = new ArrayList<>();
public VideoElementManager(Context context) {loadAudioFromMediaLibrary(context);loadVideoFromMediaLibrary(context);
// 查找音频private void loadAudioFromMediaLibrary(Context context) {Uri audioUri = AVStorage.Audio.Media.EXTERNAL_DATA_ABILITY_URI;DataAbilityHelper helper = DataAbilityHelper.creator(context, audioUri, false);try {ResultSet resultSet = helper.query(audioUri, null, null);LogUtil.info(TAG, "The audio result size: " + resultSet.getRowCount());processResult(resultSet);resultSet.close();} catch (DataAbilityRemoteException e) {LogUtil.error(TAG, "Query system media failed.");} finally {helper.release();
// 查找视频private void loadVideoFromMediaLibrary(Context context) {Uri videoUri = AVStorage.Video.Media.EXTERNAL_DATA_ABILITY_URI;DataAbilityHelper helper = DataAbilityHelper.creator(context, videoUri, false);try {ResultSet resultSet = helper.query(videoUri, null, null);LogUtil.info(TAG, "The video result size: " + resultSet.getRowCount());processResult(resultSet);resultSet.close();} catch (DataAbilityRemoteException e) {LogUtil.error(TAG, "Query system media failed.");} finally {helper.release();
// 处理数据到 Listprivate void processResult(ResultSet resultSet) {while (resultSet.goToNextRow()) {String path = resultSet.getString(resultSet.getColumnIndexForName(AVStorage.AVBaseColumns.DATA));String title = resultSet.getString(resultSet.getColumnIndexForName(AVStorage.AVBaseColumns.TITLE));AVDescription bean =new AVDescription.Builder().setTitle(title).setIMediaUri(Uri.parse(path)).setMediaId(path).build();avElements.add(new AVElement(bean, AVElement.AVELEMENT_FLAG_PLAYABLE));

简单的播放功能实现

通过以上的步骤,我们已经获取了媒体资源权限,并且获取到本地的所有音视频,下面我们将通过调用系统 API 实现简单的播放功能。

下图为我们将要实现的媒体播放器界面:

通过上面的 UI 界面,我们可以清晰的看出要实现哪些简单功能,例如播放、暂停、恢复、跳转、音量调节、倍速播放和播放进度更新。

下面将给大家详细介绍以上这些功能的具体实现方法。

1、渲染 View 准备

需要播放媒体资源,特别是视频资源,我们当然需要有 View 去渲染,鸿蒙系统 API 需要设置的 View 是 Surface,我们通过以下方法获取 Surface。


// 声明 SurfaceProvider, Surfaceprivate SurfaceProvider surfaceProvider;private DirectionalLayout playViewLayout;private Surface surface;
private void addSurfaceProvider() {surfaceProvider = new SurfaceProvider(this);
if (surfaceProvider.getSurfaceOps().isPresent()) {surfaceProvider.getSurfaceOps().get().addCallback(new SurfaceCallBack());surfaceProvider.pinToZTop(true);
// 生成 Surface 的回调class SurfaceCallBack implements SurfaceOps.Callback {@Overridepublic void surfaceCreated(SurfaceOps callbackSurfaceOps) {if (surfaceProvider.getSurfaceOps().isPresent()) {surface = surfaceProvider.getSurfaceOps().get().getSurface();LogUtil.info(TAG, "surface set");
@Overridepublic void surfaceChanged(SurfaceOps callbackSurfaceOps, int format, int width, int height) {LogUtil.info(TAG, "surface changed");
@Overridepublic void surfaceDestroyed(SurfaceOps callbackSurfaceOps) {LogUtil.info(TAG, "surface destroyed");

playViewLayout = (DirectionalLayout) findComponentById(ResourceTable.Id_playViewLayout);playViewLayout.addComponent(surfaceProvider);

2、播放

我们获取到 Surface 后就可以调用系统 API 进行播放了。以下是对系统 API 的简单封装,我们可以调用 play 接口播放媒体资源。

private Player videoPlayer;private Runnable videoRunnable;
public synchronized void play(AVElement avElement, Surface surface) {if (videoPlayer != null) {// 关闭当前播放资源// release 会重置播放状态并关闭定时器videoPlayer.stop();videoPlayer.release();videoPlayer = null;
if (videoRunnable != null) {ThreadPoolManager.getInstance().cancel(videoRunnable);
videoPlayer = new Player(context);setPlayerCallback();
videoRunnable = () -> playInner(avElement, surface);ThreadPoolManager.getInstance().execute(videoRunnable);LogUtil.info(TAG, "play");
private void playInner(AVElement avElement, Surface surface) {Source source = new Source(avElement.getAVDescription().getMediaUri().toString());videoPlayer.setSource(source);videoPlayer.setVideoSurface(surface);LogUtil.info(TAG, source.getUri());
videoPlayer.prepare();videoPlayer.play();playbackState = PlaybackState.VIDEO_PLAYER_PLAYING;startTimer();// 设置 Callbackprivate void setPlayerCallback() {videoPlayer.setPlayerCallback(new Player.IPlayerCallback() {@Overridepublic void onPrepared() {if (eventHandler == null) {return;EventHandler handler = new EventHandler(EventRunner.getMainEventRunner());handler.postTask(new Runnable() {@Overridepublic void run() {eventHandler.onPrepared();
@Overridepublic void onMessage(int type, int extra) {if (eventHandler == null) {return;EventHandler handler = new EventHandler(EventRunner.getMainEventRunner());handler.postTask(new Runnable() {@Overridepublic void run() {eventHandler.onMessage(type, extra);
@Overridepublic void onError(int errorType, int errorCode) {if (eventHandler == null) {return;EventHandler handler = new EventHandler(EventRunner.getMainEventRunner());handler.postTask(new Runnable() {@Overridepublic void run() {eventHandler.onError(errorType, errorCode);
@Overridepublic void onResolutionChanged(int width, int height) {if (eventHandler == null) {return;EventHandler handler = new EventHandler(EventRunner.getMainEventRunner());handler.postTask(new Runnable() {@Overridepublic void run() {eventHandler.onResolutionChanged(width, height);
@Overridepublic void onPlayBackComplete() {playbackState = PlaybackState.VIDEO_PLAYER_PLAY_ENDED;endTimer();if (eventHandler == null) {return;EventHandler handler = new EventHandler(EventRunner.getMainEventRunner());handler.postTask(new Runnable() {@Overridepublic void run() {eventHandler.onPlayBackComplete();
@Overridepublic void onRewindToComplete() {if (eventHandler == null) {return;EventHandler handler = new EventHandler(EventRunner.getMainEventRunner());handler.postTask(new Runnable() {@Overridepublic void run() {eventHandler.onRewindToComplete();
@Overridepublic void onBufferingChange(int percent) {if (eventHandler == null) {return;EventHandler handler = new EventHandler(EventRunner.getMainEventRunner());handler.postTask(new Runnable() {@Overridepublic void run() {eventHandler.onBufferingChange(percent);
@Overridepublic void onNewTimedMetaData(Player.MediaTimedMetaData mediaTimedMetaData) {if (eventHandler == null) {return;EventHandler handler = new EventHandler(EventRunner.getMainEventRunner());handler.postTask(new Runnable() {@Overridepublic void run() {eventHandler.onNewTimedMetaData(mediaTimedMetaData);
@Overridepublic void onMediaTimeIncontinuity(Player.MediaTimeInfo mediaTimeInfo) {if (eventHandler == null) {return;EventHandler handler = new EventHandler(EventRunner.getMainEventRunner());handler.postTask(new Runnable() {@Overridepublic void run() {eventHandler.onMediaTimeIncontinuity(mediaTimeInfo);

3、暂停、恢复和跳转

实现一个媒体播放器,我们当然是需要暂停、恢复和跳转功能,以下是简单的封装。

这里有一个问题大家需要注意,虽然系统 API 中 getCurrentTime 接口返回值单位是毫秒,但是跳转接口 rewindTo 的接口参数单位是微秒。

// 暂停public synchronized void pause() {if (videoPlayer == null) {return;videoPlayer.pause();playbackState = PlaybackState.VIDEO_PLAYER_PAUSING;endTimer();LogUtil.info(TAG, "pause");
// 恢复public synchronized void resume() {if (videoPlayer == null) {return;videoPlayer.play();playbackState = PlaybackState.VIDEO_PLAYER_PLAYING;startTimer();LogUtil.info(TAG, "resume");
// 跳转public synchronized void seekTo(long millisecond) {if (videoPlayer == null) {return;
// 注意,rewindTo 接口参数单位是微秒videoPlayer.rewindTo(millisecond * 1000);LogUtil.info(TAG, "seek" + videoPlayer.getCurrentTime());

4、时长获取和播放进度显示

我们对获取时长接口做了简单的封装,但是系统 API 中没有相关的播放进度回调,为了进行 UI 展示,我们需要自己维护一个播放状态和定时器去定时获取当前播放进度。

// 获取资源总时长,需要收到 onPrepared 回调后调用public synchronized int getDuration() {if (videoPlayer == null) {return 0;
return videoPlayer.getDuration();
private Timer timer;private PlaybackState playbackState;
private void startTimer() {if (timer == null) {timer = new Timer("PlaybackPorgressTimer");timer.schedule(new TimerTask() {@Overridepublic void run() {playbackProgressUpdate();}, 200L, 200L);
private void endTimer() {if (timer == null) {return;timer.cancel();timer = null;
private void playbackProgressUpdate() {if (eventHandler == null || videoPlayer == null) {return;int currentPlaybackProgress = videoPlayer.getCurrentTime();EventHandler handler = new EventHandler(EventRunner.getMainEventRunner());handler.postTask(new Runnable() {@Overridepublic void run() {eventHandler.onPlaybackProgressUpdate(currentPlaybackProgress);

5、音量调节和倍速播放

鸿蒙播放器系统 API 同样暴露了音量调节和倍速播放的接口,不过需要注意的是音量调节的系统 API 参数范围是 0.0~1.0,改接口仅控制播放器的音量,与手机侧边音量控制无关,有兴趣的话可以自己测试一下。

public synchronized void setPlaybackVolume(int volume) {if (videoPlayer == null) {return;
videoPlayer.setVolume((float) ((float) volume/100.0));
public synchronized void setPlaybackSpeed(float speed) {if (videoPlayer == null) {return;
videoPlayer.setPlaybackSpeed(speed);
public synchronized float getPlaybackSpeed() {if (videoPlayer == null) {return 0.0f;
return videoPlayer.getPlaybackSpeed();

总结

通过调用系统 API 实现一个简单的媒体播放器,相信大家一定对 Harmony OS都有了初步的了解与认识,比如说真正意义上的跑起来一个鸿蒙 APP,中间经过了开发者账号的申请、APP 证书的申请等等。

在今年11月底,ZEGO 即构科技首发适配鸿蒙系统的 Express SDK 1.0 版本,并正式启动了公测,这其中包括摄像头、麦克风、扬声器等设备的深度兼容与适配,实现了更加稳定、快速的设备管理能力,更多详情可点击查看:

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
濮阳车祸,乘客没有一个是无辜的,雪崩时没有一片雪花是无辜的。

濮阳车祸,乘客没有一个是无辜的,雪崩时没有一片雪花是无辜的。

阿纂看事
2025-02-17 09:23:02
四川乐山城管凌晨撬门运走搅拌机?城管回应:依规执法 选择凌晨是为了避免激化矛盾

四川乐山城管凌晨撬门运走搅拌机?城管回应:依规执法 选择凌晨是为了避免激化矛盾

封面新闻
2025-02-16 18:56:09
尹力将率中共代表团访问新加坡、柬埔寨

尹力将率中共代表团访问新加坡、柬埔寨

新京报
2025-02-17 09:54:58
热搜爆了!25岁女星突然死亡,警方介入!网友叹息:一手好牌打稀烂…

热搜爆了!25岁女星突然死亡,警方介入!网友叹息:一手好牌打稀烂…

鲁中晨报
2025-02-17 07:07:20
强渡乌江的功臣,后因不满职务脱离八路军,毛主席说情:给条活路

强渡乌江的功臣,后因不满职务脱离八路军,毛主席说情:给条活路

史小纪
2025-02-16 09:39:55
回顾:巴厘岛情侣自杀案:男友将女子按浴缸溺亡,惨叫持续3小时

回顾:巴厘岛情侣自杀案:男友将女子按浴缸溺亡,惨叫持续3小时

飞云如水
2025-02-14 23:03:38
太惨了!甘肃银行上市前员工被迫贷款入股?爆料称人均10万如今亏80%...

太惨了!甘肃银行上市前员工被迫贷款入股?爆料称人均10万如今亏80%...

金石随笔
2025-02-17 00:39:20
“独处30天赢40万大奖”,自律挑战还是“鱿鱼游戏”?

“独处30天赢40万大奖”,自律挑战还是“鱿鱼游戏”?

新京报
2025-02-17 08:05:11
预定普斯卡什奖?24岁安东尼飙凌空摆尾式世界波,现场多视角还原

预定普斯卡什奖?24岁安东尼飙凌空摆尾式世界波,现场多视角还原

侧身凌空斩
2025-02-17 06:55:15
51年之耻!曼联堕落无底线:大爷球造4大耻辱 遭热刺三杀+12轮8负

51年之耻!曼联堕落无底线:大爷球造4大耻辱 遭热刺三杀+12轮8负

风过乡
2025-02-17 06:45:19
排队3小时才能进店!上海门店无任何折扣,有人不解:哪里都能买,为什么要费劲排队?

排队3小时才能进店!上海门店无任何折扣,有人不解:哪里都能买,为什么要费劲排队?

上观新闻
2025-02-17 11:33:22
俄乌冲突迎来转机?特朗普透露:普京希望停止战争!

俄乌冲突迎来转机?特朗普透露:普京希望停止战争!

财联社
2025-02-17 10:07:06
定调!2025年养老金上涨 发放时间 预计在6月底前

定调!2025年养老金上涨 发放时间 预计在6月底前

涵豆说历史
2025-02-17 09:39:41
英超:曼联0-1热刺惨遭3杀!麦迪逊破门 加纳乔错失必进球

英超:曼联0-1热刺惨遭3杀!麦迪逊破门 加纳乔错失必进球

念洲
2025-02-17 02:30:55
游客称遭强迫购物、大巴雪天停无人区?成都文旅通报

游客称遭强迫购物、大巴雪天停无人区?成都文旅通报

界面新闻
2025-02-17 07:07:28
4岁小网红“瑶一瑶”被绊倒视频引摆拍争议,其母回应!账号粉丝超2000万,平台显示广告报价最高55万元/条,近3月发了6条

4岁小网红“瑶一瑶”被绊倒视频引摆拍争议,其母回应!账号粉丝超2000万,平台显示广告报价最高55万元/条,近3月发了6条

每日经济新闻
2025-02-16 22:59:08
京东外卖仅上线五六天,骑手增至130万!网友:美团两天出走20万

京东外卖仅上线五六天,骑手增至130万!网友:美团两天出走20万

火山诗话
2025-02-16 11:14:25
女生“全损课本”流出,自以为是清北料子,老师:上大专都费劲

女生“全损课本”流出,自以为是清北料子,老师:上大专都费劲

熙熙说教
2025-02-16 15:47:46
权威机构调查:因大量使用未经验证的设计,中国车质量明显下降!

权威机构调查:因大量使用未经验证的设计,中国车质量明显下降!

少数派报告Report
2025-02-17 10:08:04
法国小伙带622张日军侵华照片抵达北京,希望捐赠给中国

法国小伙带622张日军侵华照片抵达北京,希望捐赠给中国

齐鲁壹点
2025-02-16 17:46:16
2025-02-17 12:04:49
即构科技ZEGO
即构科技ZEGO
一站式的实时音视频通讯云服务
568文章数 60关注度
往期回顾 全部

科技要闻

官宣!百度搜索将接入DeepSeek

头条要闻

媒体:俄乌一旦和谈成功 泽连斯基应担心会否受到清算

头条要闻

媒体:俄乌一旦和谈成功 泽连斯基应担心会否受到清算

体育要闻

詹姆斯因腿部不适 不会出战全明星正赛

娱乐要闻

热搜爆了!25岁女星突然死亡,警方介入

财经要闻

"王炸组合"来了!DeepSeek朋友圈挤爆了

汽车要闻

2月底上市 小米SU7 Ultra银色实车曝光

态度原创

本地
游戏
数码
艺术
公开课

本地新闻

非遗版春节|古法滚元宵,年味儿一口爆浆

《仙剑世界》预下载&预创角开启 2月19日开服

数码要闻

2024年中国笔记本电脑线上TOP5公布:联想系居第一

艺术要闻

故宫珍藏的墨迹《十七帖》,比拓本更精良,这才是地道的魏晋写法

公开课

李玫瑾:为什么性格比能力更重要?

无障碍浏览 进入关怀版