当前位置:   article > 正文

鸿蒙视频播放器可以播放暂停重播拖拽

鸿蒙视频播放器

鸿蒙系统HarmonyOS,今天来写一个视频播放器。

官方给的例子比较简单。

官方示例参考

鸿蒙OS 视频播放开发指导_w3cschool


本例可以实现视频播放、暂停、重播、画面显示、拖拽视频进度。

看最后界面。

 

图1本文不细讲,它是增加了弹框授权。

图2是开始状态,图三是播放状态。

图2现在没有视频预览,有空再做。

准备。

需要准备resources/rawfile/video_1.mp4, 
resources/base/graphic/icon_play.xml, 
resources/base/graphic/icon_pause.xml, 
resources/base/graphic/icon_redo.xml, 
4个文件。
其中3个xml文件是用svg文件转化成xml文件的。
先在iconfont-阿里巴巴矢量图标库网上下载几个播放、暂停图标文件,选择下载svg文件,
然后在DevEco Studio->module右键->New->Svg To xml,就能把svg文件转化成xml文件了。

下面给个resources/base/graphic/icon_play.xml的示例。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <vector xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:width="200vp" ohos:height="200vp" ohos:viewportWidth="1024" ohos:viewportHeight="1024">
  3. <path ohos:fillColor="#FF000000" ohos:pathData="M157.54,860.55V163.45c0,-19.69 25.6,-33.48 43.32,-17.72l653.78,340.68c15.75,11.82 15.75,37.42 0,49.23L200.86,880.25c-17.72,13.78 -43.32,1.97 -43.32,-19.69z"></path>
  4. </vector>

在DevEco Studio中新建一个Ability吧,取名SampleVideoPlayerAbility,
会得到
src/main/java/com/example/myapplication/slice/SampleVideoPlayerAbilitySlice.java, 
src/main/java/com/example/myapplication/video/SampleVideoPlayerAbility.java, 
resources/base/layout/ability_sample_video_player.xml, 
3个文件。
本例中,实现代码在SampleVideoPlayerAbilitySlice.java文件中
布局在文件ability_sample_video_player.xml中。

上布局文件ability_sample_video_player.xml。

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <DirectionalLayout
  3. xmlns:ohos="http://schemas.huawei.com/res/ohos"
  4. ohos:height="match_parent"
  5. ohos:width="match_parent"
  6. ohos:alignment="center"
  7. ohos:orientation="vertical">
  8. <SurfaceProvider
  9. ohos:id="$+id:sp"
  10. ohos:height="200vp"
  11. ohos:width="match_parent"
  12. >
  13. </SurfaceProvider>
  14. <DependentLayout
  15. ohos:height="match_content"
  16. ohos:width="match_parent"
  17. ohos:alignment="center"
  18. >
  19. <Slider
  20. ohos:id="$+id:slider"
  21. ohos:height="40vp"
  22. ohos:width="match_parent"
  23. ohos:max="6"
  24. ohos:min="0"
  25. ohos:progress="2"
  26. ohos:progress_element="#00FF00"
  27. ohos:progress_hint_text_alignment="end"
  28. ohos:progress_hint_text_color="#ff0000"
  29. ohos:progress_hint_text_size="20fp"
  30. >
  31. </Slider>
  32. <Text
  33. ohos:id="$+id:text_slider_max"
  34. ohos:height="match_content"
  35. ohos:width="match_content"
  36. ohos:align_parent_end="true"
  37. ohos:background_element="$graphic:background_ability_main_video"
  38. ohos:below="$id:slider"
  39. ohos:layout_alignment="horizontal_center"
  40. ohos:text="0"
  41. ohos:text_size="20vp"
  42. />
  43. <Text
  44. ohos:id="$+id:text_slider_current"
  45. ohos:height="match_content"
  46. ohos:width="match_content"
  47. ohos:background_element="$graphic:background_ability_main_video"
  48. ohos:below="$id:slider"
  49. ohos:layout_alignment="horizontal_center"
  50. ohos:text="0s"
  51. ohos:text_size="20vp"
  52. />
  53. </DependentLayout>
  54. <DirectionalLayout
  55. ohos:height="match_content"
  56. ohos:width="match_content"
  57. ohos:alignment="center"
  58. ohos:orientation="horizontal">
  59. <Button
  60. ohos:id="$+id:button_play_or_pause"
  61. ohos:height="50vp"
  62. ohos:width="50vp"
  63. ohos:background_element="$graphic:icon_play"
  64. ></Button>
  65. <Button
  66. ohos:id="$+id:button_restart"
  67. ohos:height="50vp"
  68. ohos:width="50vp"
  69. ohos:background_element="$graphic:icon_redo"
  70. ></Button>
  71. </DirectionalLayout>
  72. </DirectionalLayout>

上Slice.java文件。重点部分在注释里说明了。

  1. package com.example.myapplication.slice;
  2. import com.example.myapplication.ResourceTable;
  3. import com.example.myapplication.utils.Common;
  4. import ohos.aafwk.ability.AbilitySlice;
  5. import ohos.aafwk.content.Intent;
  6. import ohos.agp.components.Button;
  7. import ohos.agp.components.Component;
  8. import ohos.agp.components.Slider;
  9. import ohos.agp.components.Text;
  10. import ohos.agp.components.element.Element;
  11. import ohos.agp.components.element.VectorElement;
  12. import ohos.agp.components.surfaceprovider.SurfaceProvider;
  13. import ohos.agp.graphics.SurfaceOps;
  14. import ohos.agp.utils.LayoutAlignment;
  15. import ohos.agp.window.dialog.ToastDialog;
  16. import ohos.eventhandler.EventHandler;
  17. import ohos.eventhandler.EventRunner;
  18. import ohos.eventhandler.InnerEvent;
  19. import ohos.global.resource.RawFileEntry;
  20. import ohos.media.common.Source;
  21. import ohos.media.player.Player;
  22. import java.io.File;
  23. import java.io.FileDescriptor;
  24. import java.io.FileInputStream;
  25. import java.io.IOException;
  26. public class SampleVideoPlayerAbilitySlice extends AbilitySlice {
  27. private Slider slider = null;
  28. private Button buttonPlay = null, buttonRestart = null;
  29. private Text textSliderMax = null;
  30. private Text textSliderCurrent = null;
  31. private SurfaceProvider sp = null;
  32. private MyEventHandler handler = null;
  33. private Player player = null;
  34. // 可选,在本例中实现视频进度拖拽
  35. private Slider.ValueChangedListener sliderValueChangedListener = new Slider.ValueChangedListener() {
  36. int position = 0;
  37. @Override
  38. public void onProgressUpdated(Slider slider, int i, boolean b) {
  39. Common.d("onProgressUpdated() " + i);
  40. position = i;
  41. //不能在此拖拽视频
  42. }
  43. @Override
  44. public void onTouchStart(Slider slider) {
  45. Common.d("onTouchStart() ");
  46. }
  47. @Override
  48. public void onTouchEnd(Slider slider) {
  49. if (player != null) {
  50. Common.d("onTouchEnd()getCurrentTime()=" + player.getCurrentTime());
  51. } else {
  52. Common.d("onTouchEnd() ");
  53. }
  54. //拖拽视频
  55. player.rewindTo(position * 1000);
  56. }
  57. };
  58. // 播放完成 、播放出错、拖拽完成回调函数
  59. private Player.IPlayerCallback playerCallback = new Player.IPlayerCallback() {
  60. @Override
  61. public void onPrepared() {
  62. Common.d("onPrepared()");
  63. }
  64. @Override
  65. public void onMessage(int i, int i1) {
  66. Common.d("onMessage()");
  67. }
  68. @Override
  69. public void onError(int i, int i1) {
  70. Common.d("onError()");
  71. }
  72. @Override
  73. public void onResolutionChanged(int i, int i1) {
  74. Common.d("onResolutionChanged()");
  75. }
  76. @Override
  77. public void onPlayBackComplete() {
  78. //播放完毕
  79. Common.d("onPlayBackComplete()");
  80. Common.d("onPlayBackComplete()getCurrentTime()=" + player.getCurrentTime());
  81. player.pause();
  82. InnerEvent event = InnerEvent.get(handler.MSG_UPDATE_BUTTON_PAUSED);
  83. handler.sendEvent(event);
  84. }
  85. @Override
  86. public void onRewindToComplete() {
  87. // 视频player.rewindTo()执行完成
  88. Common.d("onRewindToComplete()");
  89. Common.d("onRewindToComplete()getCurrentTime()=" + player.getCurrentTime());
  90. }
  91. @Override
  92. public void onBufferingChange(int i) {
  93. Common.d("onBufferingChange()");
  94. }
  95. @Override
  96. public void onNewTimedMetaData(Player.MediaTimedMetaData mediaTimedMetaData) {
  97. Common.d("onNewTimedMetaData()");
  98. }
  99. @Override
  100. public void onMediaTimeIncontinuity(Player.MediaTimeInfo mediaTimeInfo) {
  101. if (player != null) {
  102. Common.d("onMediaTimeIncontinuity()getCurrentTime()=" + player.getCurrentTime());
  103. if (false) {
  104. //onMediaTimeIncontinuity()并不会每秒更新,所以此函数不适合更新进度条。
  105. InnerEvent event = InnerEvent.get(handler.MSG_UPDATE_PROGRESS);
  106. handler.sendEvent(event);
  107. }
  108. } else {
  109. Common.d("onMediaTimeIncontinuity()");
  110. }
  111. }
  112. };
  113. // 可选,在本例中没有使用
  114. private SurfaceOps.Callback surfaceOpsCallBack = new SurfaceOps.Callback() {
  115. @Override
  116. public void surfaceCreated(SurfaceOps surfaceOps) {
  117. //Common.d("surfaceCreated()");
  118. }
  119. @Override
  120. public void surfaceChanged(SurfaceOps surfaceOps, int i, int i1, int i2) {
  121. //Common.d("surfaceChanged()");
  122. }
  123. @Override
  124. public void surfaceDestroyed(SurfaceOps surfaceOps) {
  125. //Common.d("surfaceDestroyed()");
  126. }
  127. };
  128. private Component.ClickedListener listener = new Component.ClickedListener() {
  129. @Override
  130. public void onClick(Component component) {
  131. switch (component.getId()) {
  132. case ResourceTable.Id_button_play_or_pause:
  133. //点一下播放,再点一下暂停
  134. handlePlayOrPause();
  135. break;
  136. case ResourceTable.Id_button_restart:
  137. //从头播放、重新播放
  138. if (player == null) {
  139. } else if (player.isNowPlaying()) {
  140. rewind();
  141. } else {
  142. handlePlayOrPause();
  143. }
  144. break;
  145. }
  146. }
  147. };
  148. @Override
  149. public void onStart(Intent intent) {
  150. Common.d("video", "onStart");
  151. super.onStart(intent);
  152. //设置布局
  153. Common.d("before setUIContent");
  154. super.setUIContent(ResourceTable.Layout_ability_sample_video_player);
  155. Common.d("after setUIContent");
  156. buttonPlay = (Button) findComponentById(ResourceTable.Id_button_play_or_pause);
  157. buttonRestart = (Button) findComponentById(ResourceTable.Id_button_restart);
  158. buttonPlay.setClickedListener(listener);
  159. buttonRestart.setClickedListener(listener);
  160. //进度条可以拖拽
  161. slider = (Slider) findComponentById(ResourceTable.Id_slider);
  162. slider.setValueChangedListener(sliderValueChangedListener);
  163. // handler的处理函数运行在主线程中,用来更新界面
  164. // 如果参数传入用EventRunner.create(),则handler的处理函数在子线程中运行,并不在主线程中运行。
  165. handler = new MyEventHandler(EventRunner.current());
  166. textSliderMax = (Text) findComponentById(ResourceTable.Id_text_slider_max);
  167. textSliderCurrent = (Text) findComponentById(ResourceTable.Id_text_slider_current);
  168. //视频内容显示在SurfaceProvider组件中
  169. sp = (SurfaceProvider) findComponentById(ResourceTable.Id_sp);
  170. sp.pinToZTop(true);
  171. sp.getSurfaceOps().get().addCallback(surfaceOpsCallBack);
  172. // 可忽略,弹框授权
  173. Common.grand(this, "ohos.permission.READ_MEDIA");
  174. Common.d("onStart() ---");
  175. }
  176. @Override
  177. public void onActive() {
  178. super.onActive();
  179. }
  180. @Override
  181. public void onForeground(Intent intent) {
  182. super.onForeground(intent);
  183. }
  184. private void rewind() {
  185. player.rewindTo(0);
  186. }
  187. private void handlePlayOrPause() {
  188. Common.d("handlePlayOrPause()");
  189. try {
  190. if (player == null) {
  191. //首次播放
  192. Common.d("handlePlayOrPause() try to play from start");
  193. play();
  194. Element element = new VectorElement(this, ResourceTable.Graphic_icon_pause);
  195. buttonPlay.setBackground(element);
  196. new ToastDialog(getContext())
  197. .setText("playing")
  198. // Toast显示在界面底部
  199. .setAlignment(LayoutAlignment.BOTTOM)
  200. .show();
  201. } else if (!player.isNowPlaying()) {
  202. //暂停了或者播放完成了,则继续播放或者从头播放
  203. Common.d("handlePlayOrPause() go on playing");
  204. player.play();
  205. Element element = new VectorElement(this, ResourceTable.Graphic_icon_pause);
  206. buttonPlay.setBackground(element);
  207. new ToastDialog(getContext())
  208. .setText("playing")
  209. .setAlignment(LayoutAlignment.BOTTOM)
  210. .show();
  211. } else if (player.isNowPlaying()) {
  212. Common.d("handlePlayOrPause() try to pause");
  213. pause();
  214. Element element = new VectorElement(this, ResourceTable.Graphic_icon_play);
  215. buttonPlay.setBackground(element);
  216. new ToastDialog(getContext())
  217. .setText("paused")
  218. .setAlignment(LayoutAlignment.BOTTOM)
  219. .show();
  220. }
  221. } catch (Exception e) {
  222. e.printStackTrace();
  223. Common.d("e:" + e);
  224. }
  225. }
  226. public void preparePlayer() {
  227. // 步骤 1:实例化对象
  228. if (player == null) {
  229. player = new Player(this);
  230. }
  231. try {
  232. if (true) {
  233. //此文件放在rawfile/目录中
  234. RawFileEntry entry = getResourceManager().getRawFileEntry("resources/rawfile/vid_2.mp4");//这一行和官方例子有点不一样 请注意
  235. FileDescriptor fd = null;
  236. fd = entry.openRawFileDescriptor().getFileDescriptor();
  237. Common.d("fd=" + fd + " valid=" + fd.valid());
  238. player.setSource(entry.openRawFileDescriptor());
  239. } else {
  240. //此文件放在sdcard中,但我运行总说没有权限读取文件。
  241. FileInputStream fileInputStream = new FileInputStream(new File("/sdcard/xxx.mp4"));
  242. FileDescriptor fd = fileInputStream.getFD();
  243. Source source = new Source(fd);
  244. player.setSource(source);
  245. }
  246. player.prepare();
  247. //设置显示在sp中
  248. player.setVideoSurface(sp.getSurfaceOps().get().getSurface());
  249. //播放完成或者播放出错的回调函数
  250. player.setPlayerCallback(playerCallback);
  251. //ms为单位,视频时长
  252. int duration = player.getDuration();
  253. Common.d("duration=" + duration);
  254. slider.setMaxValue(duration);
  255. textSliderMax.setText((duration + 500) / 1000 + "s");
  256. textSliderCurrent.setText("0s");
  257. } catch (IOException e) {
  258. e.printStackTrace();
  259. }
  260. }
  261. public void play() {
  262. Common.d("play()");
  263. preparePlayer();
  264. try {
  265. //因为Player的PlayerCallback中onMediaTimeIncontinuity()并不会每秒更新,
  266. //所以在onMediaTimeIncontinuity()中更新进度条的话,进度条可能长时间不动,一动就是好几秒过去了
  267. //Player的PlayerCallback中没有一个函数是每秒更新的,
  268. //所以PlayerCallback()中不适合更新进度条。
  269. //还是用线程每秒更新吧
  270. new Thread(new Runnable() {
  271. @Override
  272. public void run() {
  273. Common.d("run() duration=" + player.getDuration());
  274. int duration = player.getDuration();
  275. for (int i = 0; i < (duration + 500) / 1000; i++) {
  276. if (player == null) break;
  277. if (player.isNowPlaying()) {
  278. InnerEvent event = InnerEvent.get(handler.MSG_UPDATE_PROGRESS);
  279. handler.sendEvent(event);
  280. }
  281. try {
  282. Thread.sleep(1000);
  283. } catch (InterruptedException e) {
  284. e.printStackTrace();
  285. }
  286. }
  287. }
  288. }).start();
  289. //set progress --
  290. player.play();
  291. } catch (Exception e) {
  292. e.printStackTrace();
  293. }
  294. }
  295. public void stop() {
  296. Common.d("stop()");
  297. if (player != null) {
  298. player.stop();
  299. player.release();
  300. player = null;
  301. }
  302. }
  303. public void pause() {
  304. if (player != null) {
  305. player.pause();
  306. }
  307. }
  308. public class MyEventHandler extends EventHandler {
  309. public final int MSG_UPDATE_PROGRESS = 1;
  310. public final int MSG_UPDATE_BUTTON_PLAYING = 2;
  311. public final int MSG_UPDATE_BUTTON_PAUSED = 3;
  312. public MyEventHandler(EventRunner runner) throws IllegalArgumentException {
  313. super(runner);
  314. }
  315. @Override
  316. protected void processEvent(InnerEvent event) {
  317. //运行在主线程中
  318. super.processEvent(event);
  319. int eventId = event.eventId;
  320. switch (eventId) {
  321. case MSG_UPDATE_PROGRESS:
  322. int duration = player.getDuration();
  323. int currentTime = player.getCurrentTime();
  324. int delta = (duration - currentTime + 500) / 1000;
  325. String t = delta + " s remaining";
  326. Common.d("hint1:" + t + " progress=" + (currentTime + 500) / 1000);
  327. slider.setProgressValue(currentTime);
  328. slider.setProgressHintText(t);
  329. textSliderCurrent.setText((currentTime + 500) / 1000 + "s");
  330. Common.d("hint2:" + slider.getProgressHintText() + " progress=" + slider.getProgress());
  331. break;
  332. case MSG_UPDATE_BUTTON_PLAYING:
  333. Element element = new VectorElement(SampleVideoPlayerAbilitySlice.this, ResourceTable.Graphic_icon_pause);
  334. buttonPlay.setBackground(element);
  335. break;
  336. case MSG_UPDATE_BUTTON_PAUSED:
  337. Element elementPaused = new VectorElement(SampleVideoPlayerAbilitySlice.this, ResourceTable.Graphic_icon_play);
  338. buttonPlay.setBackground(elementPaused);
  339. break;
  340. }
  341. }
  342. }
  343. }

有任何问题欢迎指正!

如果觉得有用的话,就请我喝瓶水吧~~~


 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/234577
推荐阅读
相关标签
  

闽ICP备14008679号