栏目分类:
子分类:
返回
文库吧用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
文库吧 > IT > 前沿技术 > 云计算 > Docker/k8s

DistributedVideoPlayer 分布式视频播放器(二)

Docker/k8s 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力



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

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

https://harmonyos.51cto.com

介绍

上一期我们实现了视频的播放功能,播放列表还有评论功能.这一期,我们来看一下手机端是如何实现一个对远端TV视频播放的遥控功能.

[本文正在参与优质创作者激励]

效果展示

DistributedVideoPlayer 分布式视频播放器(二)-鸿蒙HarmonyOS技术社区

搭建环境

安装DevEco Studio,详情请参考DevEco Studio下载。

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

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

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

下载源码后,使用DevEco 打开项目。

代码结构

手机端

Java后台

  1. │  config.json │ 
  2. ├─java │  └─com 
  3. │      └─buty │          └─distributedvideoplayer 
  4. │              │  MainAbility.java                │              │  MyApplication.java 
  5. │              │ │              ├─ability 
  6. │              │      DevicesSelectAbility.java       #可流转的设备列表 │              │      MainAbilitySlice.java           #视频播放列表页 
  7. │              │      SyncControlServiceAbility.java  #同步控制服务,TV-->Phone │              │      VideoPlayAbility.java           #视频播放Ability 
  8. │              │      VideoPlayAbilitySlice.java      #视频播放详情和评论页 │              │ 
  9. │              ├─components │              │      EpisodesSelectionDialog.java     
  10. │              │      RemoteController.java           #远端控制器 │              │      VideoPlayerPlaybackButton.java  #播放按钮组件 
  11. │              │      VideoPlayerSlider.java          #播放时间进度条 │              │ 
  12. │              ├─constant │              │      Constants.java                  #常量 
  13. │              │      ResolutionEnum.java             #分辨率枚举 │              │      RouteRegister.java              #自定义路由 
  14. │              │ │              ├─data 
  15. │              │      VideoInfo.java                  #视频基础信息 │              │      VideoInfoService.java           #视频信息服务,用于模拟数据 
  16. │              │      Videos.java                     #视频列表 │              │  
  17. │              ├─model │              │      CommentModel.java               #评论模型 
  18. │              │      DeviceModel.java                #设备模型 │              │      ResolutionModel.java            #解析度模型 
  19. │              │      VideoModel.java                 #视频模型 │              │ 
  20. │              ├─provider │              │      CommentItemProvider.java        #评论数据提供程序 
  21. │              │      DeviceItemProvider.java         #设备列表提供程序 │              │      ResolutionItemProvider.java     #解析度数据提供程序 
  22. │              │      VideoItemProvider.java          #视频数据提供程序 │              │ 
  23. │              └─utils │                      AppUtil.java                   #工具类 
  24. │                      DateUtils.java 

页面布局

  1. │  │    │  ├─layout 
  2.    │  │      ability_main.xml                                #播放列表布局    │  │      comments_item.xml                               #单条评论布局 
  3.    │  │      dialog_playlist.xml                         │  │      dialog_resolution_list.xml 
  4.    │  │      dialog_table_layout.xml    │  │      hm_sample_ability_video_box.xml                 #视频播放组件页 
  5.    │  │      hm_sample_ability_video_comments.xml            #播放详情布局页    │  │      hm_sample_view_video_box_seek_bar_style1.xml    #播放进度条布局 
  6.    │  │      hm_sample_view_video_box_seek_bar_style2.xml    │  │      remote_ability_control.xml                      #远程控制器布局 
  7.    │  │      remote_ability_episodes.xml                 │  │      remote_ability_select_devices.xml               #可流转设备列表布局 
  8.    │  │      remote_ability_sound_equipment.xml              │  │      remote_device_item.xml                          #设备子项显示布局 
  9.    │  │      remote_episodes_item.xml    │  │      remote_video_quality_item.xml 
TV端

Java后台

  1. ├─main  │  │  config.json 
  2.  │  │  │  ├─java 
  3.  │  │  └─com  │  │      └─buty 
  4.  │  │          └─distributedvideoplayer  │  │              │  MainAbility.java 
  5.  │  │              │  MyApplication.java  │  │              │  VideoControlServiceAbility.java      #视频控制服务  Phone--->TV 
  6.  │  │              │  │  │              ├─component 
  7.  │  │              │      VideoSetting.java                  │  │              │ 
  8.  │  │              ├─constant                              #一些常量和枚举值  │  │              │      Constants.java                    
  9.  │  │              │      ResolutionEnum.java  │  │              │      SettingOptionEnum.java 
  10.  │  │              │      SpeedEnum.java  │  │              │ 
  11.  │  │              ├─data  │  │              │      VideoInfo.java                  #视频基本信息 
  12.  │  │              │      VideoInfoService.java           #视频数据服务,读取json中的数据  │  │              │      Videos.java                     #视频对象 
  13.  │  │              │  │  │              ├─model                                #一些数据模型 
  14.  │  │              │      ResolutionModel.java  │  │              │      SettingComponentTag.java 
  15.  │  │              │      SettingModel.java  │  │              │      VideoModel.java 
  16.  │  │              │  │  │              ├─provider 
  17.  │  │              │      SettingProvider.java  │  │              │      VideoEpisodesSelectProvider.java 
  18.  │  │              │      VideoSettingProvider.java  │  │              │ 
  19.  │  │              ├─slice  │  │              │      MainAbilitySlice.java            
  20.  │  │              │      VideoPlayAbilitySlice.java      #视频播放能力页  │  │              │  
  21.  │  │              ├─utils  │  │              │      AppUtil.java 
  22.  │  │              │  │  │              └─view 
  23.  │  │                      VideoPlayerPlaybackButton.java  │  │                      VideoPlayerSlider.java 

页面布局

  1. │      │  │    │      │  ├─layout 
  2.    │      │  │      ability_main.xml    │      │  │      ability_video_box.xml                  #播放器布局页面 
  3.    │      │  │      video_common_item.xml    │      │  │      video_episodes_item.xml                 
  4.    │      │  │      video_setting.xml    │      │  │      video_setting_item.xml 
  5.    │      │  │      view_video_box_seek_bar_style1.xml    #播放器进度条布局 
实现步骤

1.手机端

1.1.页面布局,控制器布局页 remote_ability_control.xml

使用了DependentLayout,DirectionalLayout,TableLayout 布局组件 和 其他常用的组件.


DistributedVideoPlayer 分布式视频播放器(二)-鸿蒙HarmonyOS技术社区

  1.      xmlns:ohos="http://schemas.huawei.com/res/ohos"     ohos:height="match_parent" 
  2.     ohos:width="match_parent"     ohos:clickable="true"> 
  3.             ohos:width="match_parent"         ohos:background_element="$graphic:background_ability_control_bg" 
  4.         ohos:orientation="vertical">                     ohos:id="$+id:control_app_bar"             ohos:height="match_content" 
  5.             ohos:width="match_parent">  
  6.                             ohos:height="56vp"                 ohos:width="match_content" 
  7.                 ohos:layout_alignment="vertical_center"                 ohos:orientation="horizontal"> 
  8.                                     ohos:height="$float:default_image_size"                     ohos:width="$float:default_image_size" 
  9.                     ohos:foreground_element="$media:ic_back"                     ohos:layout_alignment="center" 
  10.                     ohos:start_margin="$float:default_margin">                  
  11.                                      ohos:id="$+id:app_bar_device_name"                     ohos:height="match_parent" 
  12.                     ohos:width="match_content"                     ohos:start_margin="12vp" 
  13.                     ohos:text=""                     ohos:text_color="$color:default_white_color" 
  14.                     ohos:text_size="$float:normal_text_size_20"                     ohos:truncation_mode="ellipsis_at_end"/> 
  15.                       
  16.                     ohos:width="match_parent"             ohos:layout_alignment="vertical_center" 
  17.             ohos:orientation="horizontal">                             ohos:height="16vp"                 ohos:width="16vp" 
  18.                 ohos:foreground_element="$media:ic_play"                 ohos:layout_alignment="center" 
  19.                 ohos:start_margin="$float:default_margin">              
  20.                              ohos:id="$+id:device_video_desc"                 ohos:height="match_content" 
  21.                 ohos:width="match_parent"                 ohos:auto_scrolling_count="unlimited" 
  22.                 ohos:end_margin="$float:default_margin"                 ohos:start_margin="16vp" 
  23.                 ohos:text=""                 ohos:text_color="$color:default_white_color" 
  24.                 ohos:text_size="$float:little_text_size_12"                 ohos:truncation_mode="auto_scrolling"/> 
  25.                              ohos:id="$+id:control_middle_panel"             ohos:height="225vp" 
  26.             ohos:width="225vp"             ohos:background_element="$graphic:background_ability_control_middle" 
  27.             ohos:layout_alignment="center"             ohos:orientation="vertical" 
  28.             ohos:top_margin="64vp">                             ohos:id="$+id:control_middle_panel_top"                 ohos:height="75vp" 
  29.                 ohos:width="match_parent">                                     ohos:id="$+id:control_voice_up"                     ohos:height="$float:default_image_size" 
  30.                     ohos:width="$float:default_image_size"                     ohos:background_element="$graphic:background_button_click" 
  31.                     ohos:foreground_element="$media:ic_voice"                     ohos:layout_alignment="center" 
  32.                     ohos:top_margin="28vp"/>              
  33.                             ohos:height="75vp"                 ohos:width="match_parent" 
  34.                 ohos:orientation="horizontal">                                     ohos:id="$+id:control_backword_parent"                     ohos:height="match_parent" 
  35.                     ohos:width="75vp"                     ohos:alignment="vertical_center"> 
  36.                                             ohos:height="$float:default_image_size"                         ohos:width="$float:default_image_size" 
  37.                         ohos:background_element="$graphic:background_button_click"                         ohos:foreground_element="$media:ic_anthology" 
  38.                         ohos:layout_alignment="center"/>  
  39.                                                      ohos:id="$+id:control_play_parent"                     ohos:height="match_parent" 
  40.                     ohos:width="75vp"                     ohos:alignment="center"> 
  41.                                             ohos:height="45vp"                         ohos:width="45vp" 
  42.                         ohos:background_element="$graphic:background_ability_control_ok"                         ohos:image_src="$media:ic_pause_black" 
  43.                         ohos:layout_alignment="center"/>                  
  44.                                     ohos:height="match_parent"                     ohos:width="75vp" 
  45.                     ohos:alignment="vertical_center">                                             ohos:id="$+id:control_forward"                         ohos:height="$float:default_image_size" 
  46.                         ohos:width="$float:default_image_size"                         ohos:background_element="$graphic:background_button_click" 
  47.                         ohos:foreground_element="$media:ic_anthology"                         ohos:layout_alignment="center" 
  48.                         ohos:rotate="180"/>                  
  49.                                          ohos:id="$+id:control_middle_panel_bottom"                 ohos:height="75vp" 
  50.                 ohos:width="match_parent">                                     ohos:id="$+id:control_voice_down"                     ohos:height="$float:default_image_size" 
  51.                     ohos:width="$float:default_image_size"                     ohos:background_element="$graphic:background_button_click" 
  52.                     ohos:foreground_element="$media:ic_voice"                     ohos:layout_alignment="center" 
  53.                     ohos:top_margin="23vp"/>              
  54.                              ohos:height="0vp"             ohos:width="match_parent" 
  55.             ohos:alignment="vertical_center"             ohos:orientation="horizontal" 
  56.             ohos:weight="2">                             ohos:id="$+id:control_current_time"                 ohos:height="match_content" 
  57.                 ohos:width="match_content"                 ohos:end_margin="4vp" 
  58.                 ohos:start_margin="$float:default_margin"                 ohos:text="" 
  59.                 ohos:text_color="$color:default_white_color"                 ohos:text_size="12vp"/> 
  60.                              ohos:id="$+id:control_progress"                 ohos:height="10vp" 
  61.                 ohos:width="0vp"                 ohos:orientation="horizontal" 
  62.                 ohos:progress_color="#FF6103"                 ohos:thumb_element="$graphic:background_slide_thumb" 
  63.                 ohos:weight="5"/>  
  64.                             ohos:height="match_content"                 ohos:width="match_content" 
  65.                 ohos:end_margin="$float:default_margin"                 ohos:text="" 
  66.                 ohos:text_color="$color:default_white_color"                 ohos:text_size="12vp"/> 
  67.           
  68.                     ohos:width="match_parent"             ohos:bottom_margin="48vp" 
  69.             ohos:start_margin="16vp"             ohos:top_margin="48vp"> 
  70.                             ohos:width="match_parent">                                     ohos:height="match_parent"                     ohos:width="match_parent"> 
  71.                                             ohos:width="match_parent"                         ohos:text="$string:control_episodes" 
  72.                         ohos:text_alignment="vertical_center"                         ohos:text_color="#000000" 
  73.                         ohos:text_size="18fp"/>                  
  74.                                     ohos:width="match_parent"                     ohos:alignment="right" 
  75.                     ohos:orientation="horizontal">                                             ohos:id="$+id:control_episodes_num"                         ohos:height="match_parent" 
  76.                         ohos:width="match_content"                         ohos:background_element="$graphic:background_button_click" 
  77.                         ohos:text=""                         ohos:text_color="$color:default_black_color" 
  78.                         ohos:text_size="14fp"/>                                             ohos:id="$+id:control_all_episodes"                         ohos:height="$float:default_image_size" 
  79.                         ohos:width="$float:default_image_size"                         ohos:background_element="$graphic:background_button_click" 
  80.                         ohos:end_margin="8vp"                         ohos:foreground_element="$media:ic_right_arrow" 
  81.                         ohos:layout_alignment="center"/>                  
  82.                                          ohos:id="$+id:cotrol_bottom_item"                 ohos:height="match_content" 
  83.                 ohos:width="match_parent"                 ohos:below="$id:episodes_header" 
  84.                 ohos:column_count="6"                 ohos:top_margin="12vp"> 
  85.                       
  86.       

1.2.页面布局,选择设备组件布局页 remote_ability_select_devices.xml

使用了DependentLayout,DirectionalLayout布局组件 和 ListContainer 等组件.


DistributedVideoPlayer 分布式视频播放器(二)-鸿蒙HarmonyOS技术社区

  1.      xmlns:ohos="http://schemas.huawei.com/res/ohos"     ohos:height="match_parent" 
  2.     ohos:width="match_parent"     ohos:alignment="vertical_center" 
  3.     ohos:background_element="$color:default_panel_background"     ohos:orientation="vertical"> 
  4.              ohos:height="100vp"         ohos:width="match_parent" 
  5.         ohos:background_element="$graphic:background_ability_devices"         ohos:end_margin="12vp" 
  6.         ohos:end_padding="$float:default_margin"         ohos:layout_alignment="vertical_center" 
  7.         ohos:start_margin="12vp"         ohos:start_padding="$float:default_margin"> 
  8.                     ohos:height="match_content"             ohos:width="match_parent" 
  9.             ohos:text="$string:local_machine"             ohos:text_color="$color:default_black_color" 
  10.             ohos:text_size="14fp"             ohos:top_margin="12vp"/> 
  11.                     ohos:height="$float:default_image_size"             ohos:width="$float:default_image_size" 
  12.             ohos:below="$id:devices_title"             ohos:foreground_element="$media:icon" 
  13.             ohos:top_margin="20vp"/>                     ohos:height="match_content"             ohos:width="match_parent" 
  14.             ohos:below="$id:devices_title"             ohos:end_of="$id:devices_head_icon" 
  15.             ohos:orientation="vertical"             ohos:start_padding="12vp" 
  16.             ohos:top_margin="12vp">                             ohos:id="$+id:devices_head_app_name"                 ohos:height="match_content" 
  17.                 ohos:width="match_parent"                 ohos:max_height="28vp" 
  18.                 ohos:text=""                 ohos:text_color="$color:default_black_color" 
  19.                 ohos:text_size="14fp"/>                             ohos:id="$+id:devices_head_video_name"                 ohos:height="18vp" 
  20.                 ohos:width="match_parent"                 ohos:text="" 
  21.                 ohos:text_color="#99000000"                 ohos:text_size="12fp" 
  22.                 ohos:truncation_mode="ellipsis_at_end"/>          
  23.                  ohos:height="300vp"         ohos:width="match_parent" 
  24.         ohos:background_element="$graphic:background_ability_devices"         ohos:end_margin="12vp" 
  25.         ohos:orientation="vertical"         ohos:padding="$float:default_margin" 
  26.         ohos:start_margin="12vp"         ohos:top_margin="12vp"> 
  27.                     ohos:width="match_parent"             ohos:bottom_margin="10vp" 
  28.             ohos:text="$string:my_devices"             ohos:text_color="$color:default_black_color" 
  29.             ohos:text_size="16vp"/>                     ohos:id="$+id:devices_container"             ohos:height="match_parent" 
  30.             ohos:width="match_parent"             ohos:layout_alignment="horizontal_center" 
  31.             ohos:orientation="vertical"/>      
  32.  

1.3.Java代码,远端控制器视图组件 RemoteController.java

RemoteController继承自DependentLayout布局组件,实现了Component.ClickedListener和Slider.ValueChangedListener,用于处理 点击事件 和 滑块滑动事件。

  1.  
  2. public class RemoteController extends DependentLayout         //实现了 组件的点击监听和滑块的值变化监听 的接口 
  3.         implements Component.ClickedListener, Slider.ValueChangedListener { ... 

控制器面板视图组件的组成,包括两大部分,

第一部分是:组件的初始化,包括:控制组件的初始化, 播放进度组件的初始化, 剧集组件的初始化

  1.  private void initView() { 
  2.     //设置隐藏     setVisibility(INVISIBLE); 
  3.     if (controllerView == null) {         controllerView = 
  4.                 LayoutScatter.getInstance(slice).parse(ResourceTable.Layout_remote_ability_control, this, false);     } 
  5.      //初始化文本 
  6.     initItemText();     initItemSize(); 
  7.     initItemImage();  
  8.     //进度滑块     initProgressSlider(); 
  9.     //初始化按钮     initButton(ResourceTable.Id_app_bar_back); 
  10.     initButton(ResourceTable.Id_control_episodes_num);     initButton(ResourceTable.Id_control_all_episodes); 
  11.     initButton(ResourceTable.Id_control_play);     initButton(ResourceTable.Id_control_backword); 
  12.     initButton(ResourceTable.Id_control_forward);     initButton(ResourceTable.Id_control_voice_down); 
  13.     initButton(ResourceTable.Id_control_voice_up);  
  14.     //初始化底部的显示的视频剧集     initBottomComponent(); 
  15.      //将组件追加到队列末尾 
  16.     addComponent(controllerView);  
  17.     //初始化剧集对话框     initEpisodesDialog(); 
  18.      isPlaying = true; 

第二部分是:自定义了控制监听器(RemoteControllerListener )和接口,结合点击事和滑块滑动事件将自己的操作传递给手机视频播放器类(VideoPlayAbilitySlice)。

  1.  public interface RemoteControllerListener { 
  2.      //发送控制码给该接口的实现     void sendControl(int code, String extra); 
  3. }  
  4. public void setRemoteControllerCallback(RemoteControllerListener listener) {     remoteControllerListener = listener; 
  5. }  
  6.   
  7. @Override public void onClick(Component component) { 
  8.     switch (component.getId()) {         //返回组件 
  9.         case ResourceTable.Id_app_bar_back:             hide(true); 
  10.             break;  
  11.         case ResourceTable.Id_control_episodes_num:             //剧集组件,显示剧集对话框 
  12.         case ResourceTable.Id_control_all_episodes:             episodesDialog.setVisibility(VISIBLE); 
  13.             break;             //播放组件,发送播放的控制指令 
  14.         case ResourceTable.Id_control_play:             remoteControllerListener.sendControl(ControlCode.PLAY.getCode(), ""); 
  15.             break;             //快退组件,发送快退指令 
  16.         case ResourceTable.Id_control_backword:             remoteControllerListener.sendControl(ControlCode.BACKWARD.getCode(), ""); 
  17.             break;             //快进组件,发送快进指令 
  18.         case ResourceTable.Id_control_forward:             remoteControllerListener.sendControl(ControlCode.FORWARD.getCode(), ""); 
  19.             break;             //增加音量,发送给增加音量指令 
  20.         case ResourceTable.Id_control_voice_up:             remoteControllerListener.sendControl(ControlCode.VOLUME_ADD.getCode(), ""); 
  21.             break;             //降低音量,发送降低音量指令 
  22.         case ResourceTable.Id_control_voice_down:             //关闭显示的对话框 
  23.             if (getDialogVisibility()) {                 remoteControllerListener.sendControl(ControlCode.VOLUME_REDUCED.getCode(), ""); 
  24.             }             break; 
  25.         default:             break; 
  26.     } } 
  27.   @Override 
  28. public void onProgressUpdated(Slider slider, int value, boolean fromUser) {     HiLog.debug(LABEL,"onProgressUpdated"); 
  29.     slice.getUITaskDispatcher()             .delayDispatch( 
  30.                     () -> {                         //当前播放的时间进度 
  31.                         Text currentTime =                                 (Text) controllerView.findComponentById(ResourceTable.Id_control_current_time); 
  32.                         //设置显示的时间                         currentTime.setText( 
  33.                                 DateUtils.msToString(totalTime * value / Constants.ONE_HUNDRED_PERCENT));                     }, 
  34.                     0); } 
  35.  @Override 
  36. public void onTouchStart(Slider slider) {     isSliderTouching = true; 
  37. }  
  38.   @Override 
  39. public void onTouchEnd(Slider slider) {     // The pop-up box cannot block the slider touch event. 
  40.     // This event is not processed when a dialog box is displayed.     //滑动结束,发送seek指令到远端 
  41.     if (getDialogVisibility()) {         // 
  42.         remoteControllerListener.sendControl(ControlCode.SEEK.getCode(), String.valueOf(slider.getProgress()));     } 
  43.     isSliderTouching = false; } 

1.4.Java代码,流转设备列表页面 DevicesSelectAbility.java

主要是提供设备选择列表以及选择设备后返回设备信息

  1.  
  2. public class DevicesSelectAbility extends Ability {     @Override 
  3.     public void onStart(Intent intent) {  
  4.         //请求数据流转权限         requestPermissionsFromUser(new String[]{"ohos.permission.DISTRIBUTED_DATASYNC"}, 0); 
  5.          super.onStart(intent); 
  6.         super.setUIContent(ResourceTable.Layout_remote_ability_select_devices);  
  7.         this.initPage(intent);     } 
  8.       
  9.     private void initPage(Intent intent) {         //从json中获取视频数据 
  10.         VideoInfoService videoService = new VideoInfoService(this);  
  11.         //设置app名称         Text appName = (Text) findComponentById(ResourceTable.Id_devices_head_app_name); 
  12.         appName.setText(ResourceTable.String_entry_MainAbility);  
  13.         //视频名称组件         Text videoName = (Text) findComponentById(ResourceTable.Id_devices_head_video_name); 
  14.         //当前播放视频的索引         int currentPlayingIndex = intent.getIntParam(Constants.PARAM_VIDEO_INDEX, 0) + 1; 
  15.         //当前播放视频的剧集         String playingEpisodes = 
  16.                 AppUtil.getStringResource(this, ResourceTable.String_control_playing_episodes)                         .replaceAll("\?", String.valueOf(currentPlayingIndex)); 
  17.         //设置播放视频名称和剧集         videoName.setText(videoService.getAllVideoInfo().getVideoname() + " " + playingEpisodes); 
  18.          //在线设备列表,以及设置点击的监听事件、传递数据 
  19.         ListContainer listContainer = (ListContainer) findComponentById(ResourceTable.Id_devices_container);         List devices = AppUtil.getDevicesInfo(); 
  20.          //容器绑定数据提供程序 
  21.         DeviceItemProvider provider = new DeviceItemProvider(this, devices);         listContainer.setItemProvider(provider); 
  22.          //设置点击监听处理 
  23.         listContainer.setItemClickedListener(                 (container, component, position, id) -> { 
  24.                     //获取点击的item                     DeviceModel item = (DeviceModel) listContainer.getItemProvider().getItem(position); 
  25.                      //返回数据意图 
  26.                     Intent intentResult = new Intent();                     //设置要返回的参数 
  27.                     intentResult.setParam(Constants.PARAM_DEVICE_TYPE, item.getDeviceType());                     intentResult.setParam(Constants.PARAM_DEVICE_ID, item.getDeviceId()); 
  28.                     intentResult.setParam(Constants.PARAM_DEVICE_NAME, item.getDeviceName());                     //设置返回结果 
  29.                     setResult(0, intentResult);  
  30.                     //关闭当前Ability                     this.terminateAbility(); 
  31.                 });     } 

可用设备列表提供程序 DeviceItemProvider.java

  1.  
  2. public class DeviceItemProvider extends baseItemProvider {     private final Context context; 
  3.     private final List list;  
  4.          public DeviceItemProvider(Context context, List list) { 
  5.         this.context = context;         this.list = list; 
  6.     }  
  7.     @Override     public int getCount() { 
  8.         return list == null ? 0 : list.size();     } 
  9.      @Override 
  10.     public Object getItem(int position) {         if (list != null && position >= 0 && position < list.size()) { 
  11.             return list.get(position);         } 
  12.         return new DeviceModel();     } 
  13.      @Override 
  14.     public long getItemId(int position) {         return position; 
  15.     }  
  16.     @Override     public Component getComponent(int position, Component convertComponent, ComponentContainer componentContainer) { 
  17.         final Component cpt;         if (convertComponent == null) { 
  18.             cpt = LayoutScatter.getInstance(context).parse(ResourceTable.Layout_remote_device_item, null, false);         } else { 
  19.             cpt = convertComponent;         } 
  20.         DeviceModel deviceItem = list.get(position);         //设备名称 
  21.         Text deviceName = (Text) cpt.findComponentById(ResourceTable.Id_device_item_name);         deviceName.setText(deviceItem.getDeviceName()); 
  22.          //设备图标 
  23.         Image deviceIcon = (Image) cpt.findComponentById(ResourceTable.Id_device_item_icon);         AppUtil.setDeviceIcon(deviceItem.getDeviceType(), deviceIcon); 
  24.          if (position == list.size() - 1) { 
  25.             Component divider = cpt.findComponentById(ResourceTable.Id_device_item_divider);             divider.setVisibility(Component.INVISIBLE); 
  26.         }  
  27.         return cpt;     } 

1.5.Java代码,视频播放器页面 VideoPlayAbilitySlice.java

视频播放器页面 远端控制操作的代码主要包括两部分,

第一部分是:点击“流转” 按钮时,打开可用设备列表,点击要流转的设备后,在onAbilityResult方法中,打开远端TV设备的播放器能力页(MainAbility) 并 连接上控制元服务(VideoControlServiceAbility)

  1.  
  2. @Override protected void onAbilityResult(int requestCode, int resultCode, Intent resultIntent) { 
  3.     HiLog.debug(LABEL, "onAbilityResult");     // 
  4.     if (requestCode == Constants.PRESENT_SELECT_DEVICES_REQUEST_CODE && resultIntent != null) {         // 
  5.         startRemoteAbilityPa(resultIntent);         return; 
  6.     }     // 
  7.     setDisplayOrientation(AbilityInfo.DisplayOrientation.values()[sourceDisplayOrientation + 1]);     if (isVideoPlaying) { 
  8.         player.start();     } 
  9. }  
  10. private void startRemoteAbilityPa(Intent resultIntent) {  
  11.     //远端TV设备ID     String devicesId = resultIntent.getStringParam(Constants.PARAM_DEVICE_ID); 
  12.     Intent intent = new Intent();     Operation operation = 
  13.             new Intent.OperationBuilder()                     .withDeviceId(devicesId) 
  14.                     .withBundleName(getBundleName())                     .withAbilityName("com.buty.distributedvideoplayer.MainAbility") 
  15.                     .withAction("action.video.play")                     .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE) 
  16.                     .build();     //本地存储设备ID 
  17.     String localDeviceId =             KvManagerFactory.getInstance().createKvManager(new KvManagerConfig(this)).getLocalDeviceInfo().getId(); 
  18.      HiLog.debug(LABEL, "remoteDevicesId:" + devicesId + ",localDeviceId:" + localDeviceId); 
  19.      //播放的视频路径 
  20.     String path =             videoService 
  21.                     .getVideoInfoByIndex(currentPlayingIndex)                     .getResolutions() 
  22.                     .get(currentPlayingResolutionIndex)                     .getUrl(); 
  23.     //本地ph()one设备ID     intent.setParam(RemoteConstant.INTENT_PARAM_REMOTE_DEVICE_ID, localDeviceId); 
  24.     //播放视频的URL     intent.setParam(RemoteConstant.INTENT_PARAM_REMOTE_VIDEO_PATH, path); 
  25.     //播放不同分辨率视频的索引     intent.setParam(RemoteConstant.INTENT_PARAM_REMOTE_VIDEO_INDEX, currentPlayingIndex); 
  26.     //播放进度位置     intent.setParam(RemoteConstant.INTENT_PARAM_REMOTE_START_POSITION, (int) player.getSeekWhenPrepared()); 
  27.     intent.setOperation(operation);     //启动远端的播放Ability 
  28.     startAbility(intent);  
  29.     //远端视频控制元服务     Intent remotePaIntent = new Intent(); 
  30.     Operation paOperation =             new Intent.OperationBuilder() 
  31.                     .withDeviceId(devicesId)                     .withBundleName(getBundleName()) 
  32.                     .withAbilityName("com.buty.distributedvideoplayer.VideoControlServiceAbility")                     .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE) 
  33.                     .build();     remotePaIntent.setOperation(paOperation); 
  34.     //连接远端视频控制服务,使用2台P40的超级终端模拟器连接不成功     //Context::connectRemoteAbility failed, errorCode is 1319 
  35.     boolean connectFlag = connectAbility(remotePaIntent, connection);  
  36.     if (connectFlag) {         HiLog.debug(LABEL, "start remote ability PA success"); 
  37.         //设置显示方向为竖屏         setDisplayOrientation(AbilityInfo.DisplayOrientation.PORTRAIT); 
  38.          //初始化远端控制 
  39.         initRemoteController();  
  40.         //设置播放进度、状态、等         remoteController.setVideoInfo( 
  41.                 resultIntent.getStringParam(Constants.PARAM_DEVICE_NAME),                 currentPlayingIndex, 
  42.                 (int) player.getCurrentPosition(),                 (int) player.getDuration()); 
  43.         remoteController.show();     } else { 
  44.         HiLog.error(LABEL, "start remote ability PA failed");         stopAbility(intent); 
  45.     } } 

第二部分是:成功连接到远端视频控制元服务后,初始化远端控制器(RemoteController)并实现控制器面板的监听器接口(sendControl),通过mProxy发送控制指令到TV端(sendDataToRemote)

  1.  private void initRemoteController() { 
  2.     if (remoteController == null) {         remoteController = new RemoteController(this); 
  3.          //手机端控制面板操作的监听回调 
  4.         remoteController.setRemoteControllerCallback(                 (code, extra) -> { 
  5.                     if (mProxy == null) {                         return; 
  6.                     }                     //发送控制指令到TV端 
  7.                     boolean result =                             mProxy.sendDataToRemote(RemoteConstant.REQUEST_CONTROL_REMOTE_DEVICE, code, extra); 
  8.                      if (!result) { 
  9.                         new ToastDialog(getContext())                                 .setText( 
  10.                                         AppUtil.getStringResource(                                                 getContext(), ResourceTable.String_send_failed_tips)) 
  11.                                 .show();                         remoteController.hide(false); 
  12.                     }                 }); 
  13.          StackLayout rootLayout = (StackLayout) findComponentById(ResourceTable.Id_root_layout); 
  14.         rootLayout.addComponent(remoteController);     } 

第三部分是:订阅手机端控制事件(Constants.PHONE_CONTROL_EVENT)用于处理同步控制服务(SyncControlServiceAbility)发过来的事件,目的是把TV端的状态同步给手机控制端

  1.  private void subscribe() { 
  2.     HiLog.debug(LABEL, "subscribe");     MatchingSkills matchingSkills = new MatchingSkills(); 
  3.     //手机端控制面板的 控制事件     matchingSkills.addEvent(Constants.PHONE_CONTROL_EVENT); 
  4.     matchingSkills.addEvent(CommonEventSupport.COMMON_EVENT_SCREEN_ON);     CommonEventSubscribeInfo subscribeInfo = new CommonEventSubscribeInfo(matchingSkills); 
  5.      //事件订阅器 TODO 
  6.     subscriber = new MyCommonEventSubscriber(subscribeInfo);     try { 
  7.         CommonEventManager.subscribeCommonEvent(subscriber);     } catch (RemoteException e) { 
  8.         HiLog.error(LABEL, "subscribeCommonEvent occur exception.");     } 
  9. }  
  10.  private void unSubscribe() { 
  11.     HiLog.debug(LABEL, "unSubscribe");     try { 
  12.         CommonEventManager.unsubscribeCommonEvent(subscriber);     } catch (RemoteException e) { 
  13.         HiLog.error(LABEL, "unsubscribecommonevent occur exception.");     } 
  14. }  
  15. class MyCommonEventSubscriber extends CommonEventSubscriber {     MyCommonEventSubscriber(CommonEventSubscribeInfo info) { 
  16.         super(info);     } 
  17.      @Override 
  18.     public void onReceiveEvent(CommonEventData commonEventData) {         Intent intent = commonEventData.getIntent(); 
  19.         //获取事件参数,控制指令码         int controlCode = intent.getIntParam(Constants.KEY_CONTROL_CODE, 0); 
  20.          HiLog.debug(LABEL,"onReceiveEvent: controlCode"+controlCode); 
  21.          //未进行远端控制 
  22.         if (remoteController == null || !remoteController.isShown()) {             HiLog.debug(LABEL, "remote controller is hidden now"); 
  23.             return;         } 
  24.         //如果是视频播放进度指令         if (controlCode == ControlCode.SYNC_VIDEO_PROCESS.getCode()) { 
  25.             int totalTime = Integer.parseInt(intent.getStringParam(Constants.KEY_CONTROL_VIDEO_TIME));             int progress = Integer.parseInt(intent.getStringParam(Constants.KEY_CONTROL_VIDEO_PROGRESS)); 
  26.             //更新的控制面板的进度条             remoteController.syncVideoPlayProcess(totalTime, progress); 
  27.          //更新控制面板的视频播放状态 
  28.         } else if (controlCode == ControlCode.SYNC_VIDEO_STATUS.getCode()) {  
  29.             boolean isPlaying =                     Boolean.parseBoolean(intent.getStringParam(Constants.KEY_CONTROL_VIDEO_PLAYBACK_STATUS)); 
  30.             if (remoteController.getPlayingStatus() != isPlaying) {                 remoteController.changePlayingStatus(); 
  31.             }  
  32.         //更新控制面板的音量         } else { 
  33.             int currentVolume = Integer.parseInt(intent.getStringParam(Constants.KEY_CONTROL_VIDEO_VOLUME));             remoteController.changeVolumeIcon(currentVolume); 
  34.          } 
  35.     } } 

1.6.Java代码,远端视频控制同步服务 SyncControlServiceAbility.java

这个服务是给TV端连接使用的,对端连接过来,将 播放状态、播放进度、音量值同步过来

  1.  
  2. public class SyncControlServiceAbility extends Ability {     private static final HiLogLabel LABEL = new HiLogLabel(0, 0, "=>SyncControlServiceAbility"); 
  3.      //远端设备代理 
  4.     private final MyRemote remote = new MyRemote(RemoteConstant.REQUEST_SYNC_VIDEO_STATUS);  
  5.     @Override     public void onStart(Intent intent) { 
  6.         super.onStart(intent);         // 
  7.         remote.setRemoteRequestCallback(                 this::sendEvent); 
  8.     }  
  9.     @Override     public void onBackground() { 
  10.         super.onBackground();     } 
  11.      @Override 
  12.     public void onStop() {         super.onStop(); 
  13.     }  
  14.     @Override     protected IRemoteObject onConnect(Intent intent) { 
  15.         super.onConnect(intent);         return remote.asObject(); 
  16.     }  
  17.          private void sendEvent(int controlCode, Map value) { 
  18.         HiLog.debug(LABEL,"sendEvent,controlCode:"+controlCode+",value:"+value.toString());         try { 
  19.             Intent intent = new Intent();             Operation operation = new Intent.OperationBuilder().withAction(Constants.PHONE_CONTROL_EVENT).build(); 
  20.             intent.setOperation(operation);             intent.setParam(Constants.KEY_CONTROL_CODE, controlCode); 
  21.             //播放进度             if (controlCode == ControlCode.SYNC_VIDEO_PROCESS.getCode()) { 
  22.                 intent.setParam(Constants.KEY_CONTROL_VIDEO_TIME,                         String.valueOf(value.get(RemoteConstant.REMOTE_KEY_VIDEO_TOTAL_TIME))); 
  23.                 intent.setParam(Constants.KEY_CONTROL_VIDEO_PROGRESS,                         String.valueOf(value.get(RemoteConstant.REMOTE_KEY_VIDEO_CURRENT_PROGRESS))); 
  24.             //播放状态             } else if (controlCode == ControlCode.SYNC_VIDEO_STATUS.getCode()) { 
  25.                 intent.setParam(Constants.KEY_CONTROL_VIDEO_PLAYBACK_STATUS,                         String.valueOf(value.get(RemoteConstant.REMOTE_KEY_VIDEO_CURRENT_PLAYBACK_STATUS))); 
  26.              //播放音量 
  27.             } else {                 intent.setParam(Constants.KEY_CONTROL_VIDEO_VOLUME, 
  28.                         String.valueOf(value.get(RemoteConstant.REMOTE_KEY_VIDEO_CURRENT_VOLUME)));             } 
  29.             CommonEventData eventData = new CommonEventData(intent);  
  30.             //发布事件             CommonEventManager.publishCommonEvent(eventData); 
  31.         } catch (RemoteException e) {             HiLog.error(LABEL, "publishCommonEvent occur exception."); 
  32.         }     } 
2.TV端

2.1.页面布局,视频播放器布局组件 ability_video_box.xml

播放器组件VideoPlayerView

  1.      xmlns:ohos="http://schemas.huawei.com/res/ohos"     ohos:id="$+id:root_layout" 
  2.     ohos:height="match_parent"     ohos:width="match_parent" 
  3.     ohos:background_element="#FFFFFFFF"     ohos:orientation="vertical"> 
  4.              ohos:id="$+id:video_view"         ohos:height="match_parent" 
  5.         ohos:width="match_parent"/>  
  6.  

2.2.页面布局,视频播放器的进度条布局组件 view_video_box_seek_bar_style1.xml

  1.   
  2.     ohos:height="54vp"     ohos:width="match_parent" 
  3.     ohos:orientation="horizontal">  
  4.             ohos:height="match_content"         ohos:width="match_content" 
  5.         ohos:above="$id:seek_bar"         ohos:align_parent_start="true" 
  6.         ohos:start_margin="12vp"         ohos:text_color="white" 
  7.         ohos:text_size="10fp"/>  
  8.             ohos:height="match_content"         ohos:width="match_parent" 
  9.         ohos:background_instruct_element="$color:seek_bar_background_instruct_color"         ohos:center_in_parent="true" 
  10.         ohos:progress_element="$color:seek_bar_progress_color"         ohos:thumb_element="$graphic:hm_sample_slider_thumb" 
  11.         ohos:vice_progress_element="$color:seek_bar_vice_progress_color"         /> 
  12.              ohos:id="$+id:end_time"         ohos:height="match_content" 
  13.         ohos:width="match_content"         ohos:above="$id:seek_bar" 
  14.         ohos:align_parent_end="true"         ohos:end_margin="12vp" 
  15.         ohos:text_color="white"         ohos:text_size="10fp"/> 
  16.  

2.3.Java代码, 视频控制元服务 VideoControlServiceAbility.java

分为两部分,

第一部分是:手机端连接过来后,asObject。

  1.  private final MyRemote remote = new MyRemote(RemoteConstant.REQUEST_CONTROL_REMOTE_DEVICE); 
  2.  @Override 
  3. protected IRemoteObject onConnect(Intent intent) {     HiLog.debug(LABEL, "onConnect"); 
  4.     super.onConnect(intent);     //返回代理对象 
  5.     return remote.asObject(); } 

 第二部分是:发送事件通知到订阅方(VideoPlayAbilitySlice)

  1.  private void sendEvent(int controlCode, Map value) { 
  2.     HiLog.debug(LABEL, "sendEvent:"+controlCode+","+value.toString());     try { 
  3.         //意图         Intent intent = new Intent(); 
  4.         //TV控制事件操作         Operation operation = new Intent.OperationBuilder() 
  5.                 .withAction(Constants.TV_CONTROL_EVENT)                 .build(); 
  6.         intent.setOperation(operation);         //设置控制参数 
  7.         intent.setParam(Constants.KEY_CONTROL_CODE, controlCode);         intent.setParam(Constants.KEY_CONTROL_VALUE, (String) value.get(RemoteConstant.REMOTE_KEY_CONTROL_VALUE)); 
  8.         //封装时间数据         CommonEventData eventData = new CommonEventData(intent); 
  9.         //通用事件管理器,发布事件         CommonEventManager.publishCommonEvent(eventData); 
  10.      } catch (RemoteException e) { 
  11.         HiLog.error(LABEL, "publishCommonEvent occur exception.");     } 

2.4.Java代码, 视频播放器能力页 VideoPlayAbilitySlice.java

第一部分是:连接手机端的同步控制元服务(SyncControlServiceAbility),建立连接后,初始化远端代理(MyRemoteProxy)。

  1. //连接的phone设备 connectRemoteDevice( 
  2.         //从意图中获取远端phone设备ID         intent.getStringParam(RemoteConstant.INTENT_PARAM_REMOTE_DEVICE_ID)); 
  3.   private void connectRemoteDevice(String deviceId) { 
  4.     HiLog.debug(LABEL,"connectRemoteDevice:"+deviceId);     Intent connectPaIntent = new Intent(); 
  5.     Operation operation =             new Intent.OperationBuilder() 
  6.                     .withDeviceId(deviceId)                     .withBundleName(getBundleName()) 
  7.                     .withAbilityName(REMOTE_PHONE_ABILITY)                     .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE) 
  8.                     .build();     connectPaIntent.setOperation(operation); 
  9.      connectAbility(connectPaIntent, connection); 
  10. }  
  11. // Creating a Connection Callback Instance private final IAbilityConnection connection = 
  12. new IAbilityConnection() {     // Callback for connecting to a service 
  13.     @Override     public void onAbilityConnectDone(ElementName elementName, IRemoteObject iRemoteObject, int resultCode) { 
  14.         myProxy = new MyRemoteProxy(iRemoteObject);     } 
  15.      // Callback for disconnecting from the service 
  16.     @Override     public void onAbilityDisconnectDone(ElementName elementName, int resultCode) { 
  17.         disconnectAbility(this);     } 
  18. }; 

 第二部分是:注册远端控制回调,实现视频播放器组件(VideoPlayerView)的RemoteControlCallback接口,使用远端代理对象(MyRemoteProxy)发送数据到手机端同步当前播放器信息

  1. //注册远端控制回调 videoBox.registerRemoteControlCallback(remoteControlCallback); 
  2.   
  3.   
  4. private VideoPlayerView.RemoteControlCallback remoteControlCallback =     new VideoPlayerView.RemoteControlCallback() { 
  5.         @Override         //进度条变化 
  6.         public void onProgressChanged(long totalTime, int progress) {             HiLog.debug(LABEL,"onProgressChanged,myProxy:"+myProxy); 
  7.             if (myProxy != null) {                 Map progressValue = new HashMap<>(); 
  8.                 //设置总时间和进度值                 progressValue.put(RemoteConstant.REMOTE_KEY_VIDEO_TOTAL_TIME, String.valueOf(totalTime)); 
  9.                 progressValue.put(RemoteConstant.REMOTE_KEY_VIDEO_CURRENT_PROGRESS, String.valueOf(progress));  
  10.                 //同步进度之给手机端的控制面板                 myProxy.sendDataToRemote( 
  11.                         RemoteConstant.REQUEST_SYNC_VIDEO_STATUS,                         ControlCode.SYNC_VIDEO_PROCESS.getCode(), 
  12.                         progressValue);             } 
  13.         }  
  14.         @Override         //播放状态变化 
  15.         public void onPlayingStatusChanged(boolean isPlaying) {             if (myProxy != null) { 
  16.                 Map videoStatusMap = new HashMap<>();                 videoStatusMap.put( 
  17.                         RemoteConstant.REMOTE_KEY_VIDEO_CURRENT_PLAYBACK_STATUS, String.valueOf(isPlaying));                 HiLog.debug(LABEL, "isPlaying = " + String.valueOf(isPlaying)); 
  18.                 myProxy.sendDataToRemote(                         RemoteConstant.REQUEST_SYNC_VIDEO_STATUS, 
  19.                         ControlCode.SYNC_VIDEO_STATUS.getCode(),                         videoStatusMap); 
  20.             }         } 
  21.          @Override 
  22.         //音量变化         public void onVolumeChanged(int volume) { 
  23.             if (myProxy != null) {                 Map volumeMap = new HashMap<>(); 
  24.                 volumeMap.put(RemoteConstant.REMOTE_KEY_VIDEO_CURRENT_VOLUME, String.valueOf(volume));                 myProxy.sendDataToRemote( 
  25.                         RemoteConstant.REQUEST_SYNC_VIDEO_STATUS,                         ControlCode.SYNC_VIDEO_VOLUME.getCode(), 
  26.                         volumeMap);             } 
  27.         } }; 

 第三部分是:订阅事件,处理视频控制服务(VideoControlServiceAbility)发送的播放器控制事件

  1.  private void subscribe() { 
  2.     HiLog.debug(LABEL,"subscribe");     MatchingSkills matchingSkills = new MatchingSkills(); 
  3.     matchingSkills.addEvent(Constants.TV_CONTROL_EVENT);     matchingSkills.addEvent(CommonEventSupport.COMMON_EVENT_SCREEN_ON); 
  4.     CommonEventSubscribeInfo subscribeInfo = new CommonEventSubscribeInfo(matchingSkills);     //订阅者 
  5.     tvSubscriber = new MyCommonEventSubscriber(subscribeInfo);     try { 
  6.         CommonEventManager.subscribeCommonEvent(tvSubscriber);     } catch (RemoteException e) { 
  7.         HiLog.error(LABEL, "subscribeCommonEvent occur exception.");     } 
  8. }  
  9.  private void unSubscribe() { 
  10.     HiLog.debug(LABEL,"subscribe");     try { 
  11.         CommonEventManager.unsubscribeCommonEvent(tvSubscriber);     } catch (RemoteException e) { 
  12.         HiLog.error(LABEL, "unSubscribe Exception");     } 
  13. }  
  14.  class MyCommonEventSubscriber extends CommonEventSubscriber { 
  15.     MyCommonEventSubscriber(CommonEventSubscribeInfo info) {         super(info); 
  16.     }  
  17.     @Override     public void onReceiveEvent(CommonEventData commonEventData) { 
  18.         HiLog.info(LABEL, "onReceiveEvent.....");  
  19.         Intent intent = commonEventData.getIntent();         int controlCode = intent.getIntParam(Constants.KEY_CONTROL_CODE, 0); 
  20.         String extras = intent.getStringParam(Constants.KEY_CONTROL_VALUE);  
  21.         //播放or暂停         if (controlCode == ControlCode.PLAY.getCode()) { 
  22.             if (videoBox.isPlaying()) {                 videoBox.pause(); 
  23.             } else if (!videoBox.isPlaying() && !needResumeStatus) {                 videoBox.start(); 
  24.             } else {                 HiLog.error(LABEL, "Ignoring the case with player status"); 
  25.             }             //拖动播放进度 
  26.         } else if (controlCode == ControlCode.SEEK.getCode()) {             videoBox.seekTo(videoBox.getDuration() * Integer.parseInt(extras) / 100); 
  27.             //快进         } else if (controlCode == ControlCode.FORWARD.getCode()) { 
  28.             videoBox.seekTo(videoBox.getCurrentPosition() + Constants.REWIND_STEP);             //快退 
  29.         } else if (controlCode == ControlCode.BACKWARD.getCode()) {             videoBox.seekTo(videoBox.getCurrentPosition() - Constants.REWIND_STEP); 
  30.             //音量加         } else if (controlCode == ControlCode.VOLUME_ADD.getCode()) { 
  31.             videoBox.setVolume(Constants.VOLUME_STEP);             //音量减 
  32.         } else if (controlCode == ControlCode.VOLUME_REDUCED.getCode()) {             videoBox.setVolume(-Constants.VOLUME_STEP); 
  33.             //切换播放速度         } else if (controlCode == ControlCode.SWITCH_SPEED.getCode()) { 
  34.             videoBox.setPlaybackSpeed(Float.parseFloat(extras));             //切换视频源,例如高清 
  35.         } else if (controlCode == ControlCode.SWITCH_RESOLUTION.getCode()) {             long currentPosition = videoBox.getCurrentPosition(); 
  36.             int resolutionIndex = Integer.parseInt(extras);             VideoInfo videoInfo = videoInfoService.getVideoInfoByIndex(currentPlayingIndex); 
  37.             videoBox.pause();  
  38.             //设置新的播放URL             videoBox.setVideoPath(videoInfo.getResolutions().get(resolutionIndex).getUrl()); 
  39.             //调整到原播放位置             videoBox.setPlayeronPreparedListener( 
  40.                     () -> {                         videoBox.seekTo(currentPosition); 
  41.                         videoBox.start();                     }); 
  42.             //切换视频         } else if (controlCode == ControlCode.SWITCH_VIDEO.getCode()) { 
  43.             videoBox.pause();             currentPlayingIndex = Integer.parseInt(extras); 
  44.             VideoInfo videoInfo = videoInfoService.getVideoInfoByIndex(currentPlayingIndex);             videoBox.setVideoPathAndTitle(videoInfo.getResolutions().get(0).getUrl(), videoInfo.getVideoDesc()); 
  45.             videoBox.setPlayeronPreparedListener(() -> videoBox.start());  
  46.             //停止连接         } else if (controlCode == ControlCode.STOP_CONNECTION.getCode()) { 
  47.             terminate();         } else { 
  48.             HiLog.error(LABEL, "Ignoring the case with control code");         } 
  49.     } } 

 至此,手机端控制端和TV端的过程就解读完了,部分细节如切换视频解析度、切换视频剧集、TV端设置 等不影响全局流程就不展开了。

两端涉及的权限如下:

  1. {     "name": "ohos.permission.INTERNET", 
  2.     "reason": "",     "usedScene": { 
  3.       "ability": [         "VideoPlayAbilitySlice" 
  4.       ],       "when": "inuse" 
  5.     }   }, 
  6.   {     "name": "ohos.permission.DISTRIBUTED_DATASYNC", 
  7.     "reason": "",     "usedScene": { 
  8.       "ability": [         "VideoPlayAbilitySlice" 
  9.       ],       "when": "inuse" 
  10.     }   }, 
  11.   {     "name": "ohos.permission.DISTRIBUTED_DEVICE_STATE_CHANGE", 
  12.     "reason": "",     "usedScene": { 
  13.       "ability": [         "VideoPlayAbilitySlice" 
  14.       ],       "when": "inuse" 
  15.     }   }, 
  16.   {     "name": "ohos.permission.GET_DISTRIBUTED_DEVICE_INFO", 
  17.     "reason": "",     "usedScene": { 
  18.       "ability": [         "VideoPlayAbilitySlice" 
  19.       ],       "when": "inuse" 
  20.     }   }, 
  21.   {     "name": "ohos.permission.GET_BUNDLE_INFO", 
  22.     "reason": "",     "usedScene": { 
  23.       "ability": [         "VideoPlayAbilitySlice" 
  24.       ],       "when": "inuse" 
  25.     }   }, 
  26.   {     "name": "ohos.permission.KEEP_BACKGROUND_RUNNING", 
  27.     "reason": "",     "usedScene": { 
  28.       "ability": [         "VideoPlayAbilitySlice" 
  29.       ],       "when": "inuse" 
  30.     }   } 
回顾总结

手机端控制TV端视频播放的流程

手机端:

点击手机端播放器(VideoPlayAbilitySlice)的【流转】按钮-------获取&选择可以流转的设备----启动 TV端播放器(MainAbility/VideoPlayAbilitySlice) & 连接TV端播放控制服务(VideoControlServiceAbility)-----在建立连接后,初始化控制面板并且对控制操作进行监听。

当操作控制面板(RemoteController)时 --发布事件通知播放器组件(VideoPlayAbilitySlice)-----(VideoPlayAbilitySlice)使用控制服务的远端代理(MyRemoteProxy,commonlib提供)发送控制指令到 TV端播放控制服务(VideoControlServiceAbility)。

TV端:

当TV端播放器(VideoPlayAbilitySlice)被启动时-----初始化视频播放器组件& 注册远端控制回调(registerRemoteControlCallback) & 获取手机端Intent传递的视频索引+视频URL+播放进度+手机端设备ID-----然后连接手机端的同步控制服务(SyncControlServiceAbility)— 在建立连接后,初始化代理(MyRemoteProxy)-----订阅手机端播放器控制事件。

当播放控制服务(VideoControlServiceAbility)收到控制指令后-----通过事件方式发布通知-----视频播放器(VideoPlayAbilitySlice)收到通知后对播放器进行设置-----注册远端控制回调(remoteControlCallback)将状态同步给远端的手机端。

文章相关附件可以点击下面的原文链接前往下载

https://harmonyos.51cto.com/resource/1356

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

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

https://harmonyos.51cto.com



 

转载请注明:文章转载自 www.wk8.com.cn
本文地址:https://www.wk8.com.cn/it/796751.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 wk8.com.cn

ICP备案号:晋ICP备2021003244-6号