在鸿蒙上实现本地和Internet视频资源播放

系统
文章由鸿蒙社区产出,想要了解更多内容请前往:51CTO和华为官方战略合作共建的鸿蒙技术社区https://harmonyos.51cto.com

[[388216]]

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

 1. 介绍

本篇Codelab将实现的内容

本篇Codelab旨在让开发者了解手机HarmonyOS应用开发,常用布局、典型控件、FA组件、媒体-视频、跨设备协同的体验以及从工程创建到代码和布局的编写,再到编译构建、部署和运行全过程。

您将构建一个基于HarmonyOS Player类实现的应用程序,该应用程序功能为播放本地视频资源或从Internet获得的视频资源。效果图如下:

您将会学到什么

● 如何使用Player类播放视频

● 如何使用自定义控件来控制视频播放

● 如何添加并使用媒体事件的事件侦听器和回调

硬件要求

● 操作系统:Windows10 64位

● 内存:8GB及以上

● 硬盘:100GB及以上

● 分辨率:1280*800像素及以上

软件要求

● 安装Huawei DevEco Studio,详情请参考下载和安装软件

● 设置Huawei DevEco Studio开发环境,Huawei DevEco Studio开发环境需要依赖于网络环境,需要连接上网络才能确保工具的正常使用,可以根据如下两种情况来配置开发环境

1.如果可以直接访问Internet,只需进行下载HarmonyOS SDK操作

2.如果网络不能直接访问Internet,需要通过代理服务器才可以访问,请参考配置开发环境

说明

如需要在手机中运行程序,则需要提前申请证书,如使用模拟器可忽略

● 生成秘钥和申请证书,详情请参考准备签名文件

技能要求

● 具备DevEco Studio中创建、构建和运行应用经验

● 熟悉Ability和AbilitySlice生命周期及使用PA/FA的能力

2. 代码结构

本篇Codelab只对核心代码进行讲解,对于完整代码,我们在参考提供下载方式。接下来我们会讲解整个工程的代码结构,如下图:

● api:视频播放状态改变及屏幕状态变化监听。

● constant:定义视频状态、进度条和控制器状态。

● factoty:创建SourceFactory类来根据视频来源创建视频源。

● manager:创建HmPlayerLifecycle来处理Player类的生命周期。

● view:创建PlayerLoading、SimplePlayerController类分别为视频加载状态及进度条控制类文件。

● HmPlayer:封装播放器的主要功能方法。

● slice:创建MainAbilitySlice、SimplePlayerAbilitySlice分别为进入应用的主程序页面和视频播放页面。

● utils:存放所有封装好的公共方法,如DateUtils,LogUtils等。

● resources:存放工程使用到的资源文件,其中resources\base\layout下存放xml布局文件;resources\base\media下存放视频文件。

● config.json:Ability声明及权限配置。

3. 创建视频播放业务逻辑

该应用程序可播放的视频格式包括mp4、mov、3gp、mkv,首先准备一份视频文件并复制到"resources/base/layout/media"文件目录。下面将会介绍视频列表布局及播放逻辑。

创建视频播放页面文件及布局

Step 1 - 创建simple_video_play_layout.xml布局文件展示视频列表。

  1. <DependentLayout  
  2.     xmlns:ohos="http://schemas.huawei.com/res/ohos"  
  3.     ohos:id="$+id:parent"  
  4.     ohos:height="match_parent"  
  5.     ohos:width="match_parent">  
  6.     <DependentLayout  
  7.         ohos:id="$+id:parent_layout"  
  8.         ohos:height="match_parent"  
  9.         ohos:width="match_parent"  
  10.         ohos:alignment="center"  
  11.         ohos:background_element="#ffffff"/>  
  12. </DependentLayout> 

该布局文件有两个id,parent是整个播放页面的布局id,parent_layout是视频画面的布局id。

Step 2 - 创建SimplePlayerAbilitySlice类,初次创建该页面进行初始化。

  1. @Override  
  2. public void onStart(Intent intent) {  
  3.     super.onStart(intent);  
  4.     super.setUIContent(ResourceTable.Layout_simple_video_play_layout);  
  5.     // 在Constants中定义视频播放的起始位置  
  6.     startMillisecond = intent.getIntParam(Constants.INTENT_STARTTIME_PARAM, 0);  
  7.     // 初始化surface布局  
  8.     initView();  
  9.     player.getLifecycle().onStart();  

将预置的视频资源初始化为url对象,并通过initView方法对视频播放的控件进行初始化及赋值。

  1. private String url = "entry/resources/base/media/gubeishuizhen.mp4";  
  2. private void initView() {  
  3.     DependentLayout playerLayout = (DependentLayout) findComponentById(ResourceTable.Id_parent_layout);  
  4.     player = new HmPlayer.Builder(this).setStartMillisecond(mStartMillisecond).setFilePath(url).create();  
  5.     playerLayout.addComponent(player.getPlayerView());  
  6.     player.play();  

—-结束

创建HmPlayer

HmPlayer类是继承自对HarmonyOS Player封装的ImPlayer。如果您还不了解HarmonyOS Player,请参考视频播放开发指导。

需要注意的是当页面初始化Player类执行play方法时,视频并没有出现画面。图像渲染在屏幕上需要使用SurfaceProvider,该类控制surface的尺寸和格式,修改surface的像素,监视surface的变化等等。当底层显示系统第一次创建surface之后会调用surfaceCreated(SurfaceOps surfaceOps)回调函数。HmPlayer中通过设置回调增加对视频的播放开始或停止控制。

  1. private SurfaceOps.Callback surfaceCallback = new SurfaceOps.Callback() {  
  2.     @Override  
  3.     public void surfaceCreated(SurfaceOps surfaceOps) {  
  4.         // 标记surfaceView状态  
  5.         isSurfaceViewCreated = true;  
  6.         surface = surfaceOps.getSurface();  
  7.         start();  
  8.     }  
  9.    
  10.     @Override  
  11.     public void surfaceChanged(SurfaceOps surfaceOps, int i, int width, int height) {  
  12.         LogUtil.info(TAG, "surfaceChanged i is " + i + ",width is " + width + ",height is " + height);  
  13.     }  
  14.    
  15.     @Override  
  16.     public void surfaceDestroyed(SurfaceOps surfaceOps) {  
  17.         LogUtil.info(TAG, "surfaceDestroyed");  
  18.         isSurfaceViewCreated = false;  
  19.     }  
  20. }; 

surfaceView的初始化在HmPlayer构造函数中:

  1. private HmPlayer(Builder builder) {  
  2.     ...  
  3.     surfaceView = new SurfaceProvider(playerBuilder.mContext);  
  4.     DependentLayout.LayoutConfig layoutConfig = new DependentLayout.LayoutConfig();  
  5.     layoutConfig.addRule(DependentLayout.LayoutConfig.CENTER_IN_PARENT);  
  6.     // 设置surfaceView布局  
  7.     surfaceView.setLayoutConfig(layoutConfig);  
  8.     surfaceView.setVisibility(Component.VISIBLE);  
  9.     surfaceView.setFocusable(Component.FOCUS_ENABLE);  
  10.     surfaceView.setTouchFocusable(true);  
  11.     surfaceView.requestFocus();  
  12.     // 设置surfaceView是否在最上方  
  13.     surfaceView.pinToZTop(playerBuilder.isTopPlay);  
  14.     surfaceView.getSurfaceOps().get().addCallback(surfaceCallback);  

在执行surfaceCreated回调时会执行HarmonyOS中Player的play方法。

  1. private void start() {  
  2.     if (isSurfaceViewCreated) {  
  3.         threadPoolExecutor.execute(() -> {  
  4.             player.setVideoSurface(surface);  
  5.             player.prepare();  
  6.             if (playerBuilder.startMillisecond > 0) {  
  7.                 int microsecond = playerBuilder.startMillisecond * MICRO_MILLI_RATE;  
  8.                 player.rewindTo(microsecond);  
  9.             } else {  
  10.                 player.play();  
  11.             }  
  12.         });  
  13.     }  

编译运行该应用程序

应用启动后,视频文件将被打开并开始播放,持续播放到最后。效果如下图:

4. 创建视频控制业务逻辑

上面的章节实现了视频播放的基本功能,本小节将创建一个控制器,包含基本的媒体控制UI元素如播放、暂停、恢复、重新加载按钮以及进度条。该控制器将与HmPlayer类一起提供一个基本功能全面且可操作的视频播放器。

创建SimpleVideoPlayerController

SimplePlayerController类为自定义组件,包括控制视频的播放、暂停、恢复以及进度条等控件。此处使用HarmonyOS EventHandler来进行UI更新,请参考HarmonyOS开发者文档线程间通信。

  1. public SimplePlayerController(Context context, ImplPlayer player) {  
  2.     super(context);  
  3.     this.context = context;  
  4.     implPlayer = player;  
  5.     // 创建子线程给自己发消息来及时更新UI  
  6.     createHandler();  
  7.     initView();  
  8.     initListener();  

其中initView方法初始化播放控制的控件。

  1. Component playerController = LayoutScatter.getInstance(context).parse(  
  2.         ResourceTable.Layout_simple_player_controller_layout, nullfalse);  
  3. addComponent(playerController);  
  4. if (playerController.findComponentById(ResourceTable.Id_play_controller) instanceof Image) {  
  5.     // 播放或者暂停按钮  
  6.     playToogle = (Image) playerController.findComponentById(ResourceTable.Id_play_controller);  
  7. }  
  8. if (playerController.findComponentById(ResourceTable.Id_play_forward) instanceof Image) {  
  9.     // 前进按钮  
  10.     imageForward = (Image) playerController.findComponentById(ResourceTable.Id_play_forward);  
  11. }  
  12. if (playerController.findComponentById(ResourceTable.Id_play_backward) instanceof Image) {  
  13.     // 后退按钮  
  14.     imageBackward = (Image) playerController.findComponentById(ResourceTable.Id_play_backward);  
  15. }  
  16. if (playerController.findComponentById(ResourceTable.Id_progress) instanceof Slider) {  
  17.     // 进度条  
  18.     progressBar = (Slider) playerController.findComponentById(ResourceTable.Id_progress);  

initListener方法是对HmPlayer和播放控制器相互之间状态变化的监听处理。

  1. implPlayer.addPlayerStatusCallback(statusChangeListener); 

添加HmPlayer状态变化的监听,例如当视频播放完毕时,回调StatusChangeListener的statusCallback来刷新对控制器中各种组件的状态和显示值。HmPlayer中HmPlayerCallback中通过底层播放回调onPlayBackComplete来对界面视频状态进行更改。

  1. @Override  
  2. public void onPlayBackComplete() {  
  3.     for (StatusChangeListener callback : statusChangeCallbacks) {  
  4.         status = PlayerStatus.COMPLETE;  
  5.         callback.statusCallback(PlayerStatus.COMPLETE);  
  6.     }  
  7.     stop();  

在SimplePlayerController的statusCallback中更新控制按钮状态。

  1. if (status == PlayerStatus.STOP || status == PlayerStatus.COMPLETE) {  
  2.     controllerHandler.sendEvent(Constants.PLAYER_PROGRESS_RUNNING, EventHandler.Priority.IMMEDIATE);  
  3.     playToogle.setPixelMap(ResourceTable.Media_ic_update);  
  4.     progressBar.setEnabled(false);  

此时播放按钮更新成待刷新图标,进度条不可拖拽。

创建PlayerLoading

在视频画面缓冲没有完成时,播放界面如果提供加载进度信息,用户体验更好。创建的PlayerLoading类设置一个布局并且添加StatusChangeListener监听回调,使得该控件可以根据状态显示或隐藏。

  1. public PlayerLoading(Context context, ImplPlayer player) {  
  2.     super(context);  
  3.     this.player = player;  
  4.     initView(context);  
  5.     initListener();  
  6. }  
  7. private void initListener() {  
  8.     player.addPlayerStatusCallback(new StatusChangeListener() {  
  9.         @Override  
  10.         public void statusCallback(PlayerStatus status) {  
  11.             //获取主线程更新UI  
  12.             mContext.getUITaskDispatcher().delayDispatch(  
  13.                     new Runnable() {  
  14.                         @Override  
  15.                         public void run() {  
  16.                             if (status == PlayerStatus.PREPARING || status == PlayerStatus.BUFFERING) {  
  17.                                 show();  
  18.                             } else if (status == PlayerStatus.PLAY) {  
  19.                                 hide();  
  20.                             } else {  
  21.                                 LogUtil.info(PlayerLoading.class.getName(), "statuCallback else message");  
  22.                             }  
  23.                         }  
  24.                     }, 0);  
  25.         }  
  26.     });  

编译运行该应用程序

经过上面的步骤,此时运行程序就可以看到一个有前进、后退、播放、暂停的界面,用户可以自主控制该视频播放,效果如下图:

5. 恭喜你

通过本篇Codelab你学到了:

● HarmonyOS中一个完整的视频播放应用需包括UI、Surface和媒体播放器。

● 使用player.setSource(source)指定视频文件的路径。

● 使用SurfaceOps.Callback来处理surface创建、状态改变和销毁的回调。

● 创建内部类HmPlayerCallback实现Player.IPlayerCallback的接口,监听视频状态改变,添加对控制器组件状态和缓冲界面的回调方法。

● 创建HmPlayerLifeCycle来管理HmPlayer生命周期。

6. 参考

gitee源码

github源码

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

 

责任编辑:jianghua 来源: 鸿蒙社区
相关推荐

2014-07-07 15:22:08

惠普公有云

2009-08-18 11:16:05

Ubuntu系统高清视频linux系统

2013-07-01 10:53:05

2024-03-14 08:24:25

MediaCodec解码播放Android

2011-07-20 16:21:20

iPhone 视频 播放器

2009-05-26 15:08:00

UbuntuFlash视频播放

2022-04-15 11:30:59

代码,Python保存视频

2021-10-29 07:49:23

Python弹幕播放

2010-03-16 14:32:07

云计算

2024-03-26 08:00:00

LLMVLMRaspberry

2024-08-28 08:48:20

Linux资源隔离

2020-06-28 14:35:54

OBSWebSockets开源

2021-08-30 07:49:32

Javascript西瓜视频

2019-09-10 16:06:46

GNOMEInternet Ra

2009-08-18 17:51:17

C#实现Interne

2011-04-16 15:57:03

云网络云服务

2022-03-06 20:02:21

监听视频播放

2022-08-26 12:13:08

APIjavascript视频

2013-12-17 11:18:53

iOS开发多媒体API

2011-03-16 11:27:31

微软IE9
点赞
收藏

51CTO技术栈公众号