• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 视频播放开发指导
2
3## 简介
4
5视频播放的主要工作是将视频数据转码并输出到设备进行播放,同时管理播放任务,包括开始播放、暂停播放、停止播放、资源释放、音量设置、跳转播放位置、设置倍数、获取轨道信息等功能控制。本文将对视频播放全流程、视频切换、视频循环播放等场景开发进行介绍说明。
6
7## 运作机制
8
9该模块提供了视频播放状态变化示意图和视频播放外部模块交互图。
10
11**图1** 视频播放状态变化示意图
12
13![zh-ch_image_video_state_machine](figures/zh-ch_image_video_state_machine.png)
14
15**图2** 视频播放外部模块交互图
16
17![zh-ch_image_video_player](figures/zh-ch_image_video_player.png)
18
19**说明**:三方应用通过调用JS接口层提供的js接口实现相应功能时,框架层会通过Native Framework的媒体服务,调用音频部件将软件解码后的音频数据,输出至音频HDI,和图形子系统将硬件接口层的解码HDI部件的解码后的图像数据,输出至显示HDI,实现视频播放功能。
20
21*注意:视频播放需要显示、音频、编解码等硬件能力。*
22
231. 三方应用从Xcomponent组件获取surfaceID。
242. 三方应用把surfaceID传递给VideoPlayer JS。
253. 媒体服务把帧数据flush给surface buffer。
26
27## 兼容性说明
28视频播放支持的视频格式分必选规格和可选规格。必选规格为所有厂商均支持的视频格式。对于可选规格,厂商将基于实际情况决定是否实现。建议开发者做兼容处理,保证全平台兼容。
29推荐使用主流的播放格式和主流分辨率,不建议开发者自制非常或者异常码流,以免产生无法播放、卡住、花屏等兼容性问题。若发生此类问题不会影响系统,退出码流播放即可。
30
31|  视频格式  |  是否必选规格  |
32|:--------:|:-----:|
33|   H264   |   是  |
34|  MPEG2   |   否  |
35|  MPEG4   |   否  |
36|   H263   |   否  |
37|    VP8   |   否  |
38
39主流的播放格式和主流分辨率如下:
40
41| 视频容器规格 |                     规格描述                      |               分辨率               |
42| :----------: | :-----------------------------------------------: | :--------------------------------: |
43|     mp4      | 视频格式:H264/MPEG2/MPEG4/H263 音频格式:AAC/MP3 | 主流分辨率,如1080P/720P/480P/270P |
44|     mkv      | 视频格式:H264/MPEG2/MPEG4/H263 音频格式:AAC/MP3 | 主流分辨率,如1080P/720P/480P/270P |
45|      ts      |   视频格式:H264/MPEG2/MPEG4 音频格式:AAC/MP3    | 主流分辨率,如1080P/720P/480P/270P |
46|     webm     |          视频格式:VP8 音频格式:VORBIS           | 主流分辨率,如1080P/720P/480P/270P |
47
48## 开发指导
49
50详细API含义可参考:[媒体服务API文档VideoPlayer](../reference/apis/js-apis-media.md#videoplayer8)
51
52### 全流程场景
53
54视频播放的全流程场景包含:创建实例,设置url,设置SurfaceId,准备播放视频,播放视频,暂停播放,获取轨道信息,跳转播放位置,设置音量,设置倍速,结束播放,重置,释放资源等流程。
55
56VideoPlayer支持的url媒体源输入类型可参考:[url属性说明](../reference/apis/js-apis-media.md#videoplayer_属性)
57
58Xcomponent创建方法可参考:[Xcomponent创建方法](../reference/arkui-ts/ts-basic-components-xcomponent.md)
59
60```js
61import media from '@ohos.multimedia.media'
62import fs from '@ohos.file.fs'
63export class VideoPlayerDemo {
64  // 函数调用发生错误时用于上报错误信息
65  failureCallback(error) {
66    console.info(`error happened,error Name is ${error.name}`);
67    console.info(`error happened,error Code is ${error.code}`);
68    console.info(`error happened,error Message is ${error.message}`);
69  }
70
71  // 当函数调用发生异常时用于上报错误信息
72  catchCallback(error) {
73    console.info(`catch error happened,error Name is ${error.name}`);
74    console.info(`catch error happened,error Code is ${error.code}`);
75    console.info(`catch error happened,error Message is ${error.message}`);
76  }
77
78  // 用于打印视频轨道信息
79  printfDescription(obj) {
80    for (let item in obj) {
81      let property = obj[item];
82      console.info('key is ' + item);
83      console.info('value is ' + property);
84    }
85  }
86
87  async videoPlayerDemo() {
88    let videoPlayer = undefined;
89    let surfaceID = 'test' // surfaceID用于播放画面显示,具体的值需要通过Xcomponent接口获取,相关文档链接见上面Xcomponent创建方法
90    let fdPath = 'fd://'
91    // path路径的码流可通过"hdc file send D:\xxx\H264_AAC.mp4 /data/app/el1/bundle/public/ohos.acts.multimedia.video.videoplayer/ohos.acts.multimedia.video.videoplayer/assets/entry/resources/rawfile" 命令,将其推送到设备上
92    let path = '/data/app/el1/bundle/public/ohos.acts.multimedia.video.videoplayer/ohos.acts.multimedia.video.videoplayer/assets/entry/resources/rawfile/H264_AAC.mp4';
93    let file = await fs.open(path);
94    fdPath = fdPath + '' + file.fd;
95    // 调用createVideoPlayer接口返回videoPlayer实例对象
96    await media.createVideoPlayer().then((video) => {
97      if (typeof (video) != 'undefined') {
98        console.info('createVideoPlayer success!');
99        videoPlayer = video;
100      } else {
101        console.info('createVideoPlayer fail!');
102      }
103    }, this.failureCallback).catch(this.catchCallback);
104    // 设置播放源
105    videoPlayer.url = fdPath;
106
107    // 设置surfaceID用于显示视频画面
108    await videoPlayer.setDisplaySurface(surfaceID).then(() => {
109      console.info('setDisplaySurface success');
110    }, this.failureCallback).catch(this.catchCallback);
111
112    // 调用prepare完成播放前准备工作
113    await videoPlayer.prepare().then(() => {
114      console.info('prepare success');
115    }, this.failureCallback).catch(this.catchCallback);
116
117    // 调用play接口正式开始播放
118    await videoPlayer.play().then(() => {
119      console.info('play success');
120    }, this.failureCallback).catch(this.catchCallback);
121
122    // 暂停播放
123    await videoPlayer.pause().then(() => {
124      console.info('pause success');
125    }, this.failureCallback).catch(this.catchCallback);
126
127    // 通过promise回调方式获取视频轨道信息communication_dsoftbus
128    let arrayDescription;
129    await videoPlayer.getTrackDescription().then((arrlist) => {
130      if (typeof (arrlist) != 'undefined') {
131        arrayDescription = arrlist;
132      } else {
133        console.log('video getTrackDescription fail');
134      }
135    }, this.failureCallback).catch(this.catchCallback);
136
137    for (let i = 0; i < arrayDescription.length; i++) {
138      this.printfDescription(arrayDescription[i]);
139    }
140
141    // 跳转播放时间到50s位置,具体入参意义请参考接口文档
142    let seekTime = 50000;
143    await videoPlayer.seek(seekTime, media.SeekMode.SEEK_NEXT_SYNC).then((seekDoneTime) => {
144      console.info('seek success');
145    }, this.failureCallback).catch(this.catchCallback);
146
147    // 音量设置接口,具体入参意义请参考接口文档
148    let volume = 0.5;
149    await videoPlayer.setVolume(volume).then(() => {
150      console.info('setVolume success');
151    }, this.failureCallback).catch(this.catchCallback);
152
153    // 倍速设置接口,具体入参意义请参考接口文档
154    let speed = media.PlaybackSpeed.SPEED_FORWARD_2_00_X;
155    await videoPlayer.setSpeed(speed).then(() => {
156      console.info('setSpeed success');
157    }, this.failureCallback).catch(this.catchCallback);
158
159    // 结束播放
160    await videoPlayer.stop().then(() => {
161      console.info('stop success');
162    }, this.failureCallback).catch(this.catchCallback);
163
164    // 重置播放配置
165    await videoPlayer.reset().then(() => {
166      console.info('reset success');
167    }, this.failureCallback).catch(this.catchCallback);
168
169    // 释放播放资源
170    await videoPlayer.release().then(() => {
171      console.info('release success');
172    }, this.failureCallback).catch(this.catchCallback);
173
174    // 相关对象置undefined
175    videoPlayer = undefined;
176    surfaceID = undefined;
177  }
178}
179```
180
181### 正常播放场景
182
183```js
184import media from '@ohos.multimedia.media'
185import fs from '@ohos.file.fs'
186export class VideoPlayerDemo {
187  // 函数调用发生错误时用于上报错误信息
188  failureCallback(error) {
189    console.info(`error happened,error Name is ${error.name}`);
190    console.info(`error happened,error Code is ${error.code}`);
191    console.info(`error happened,error Message is ${error.message}`);
192  }
193
194  // 当函数调用发生异常时用于上报错误信息
195  catchCallback(error) {
196    console.info(`catch error happened,error Name is ${error.name}`);
197    console.info(`catch error happened,error Code is ${error.code}`);
198    console.info(`catch error happened,error Message is ${error.message}`);
199  }
200
201  // 用于打印视频轨道信息
202  printfDescription(obj) {
203    for (let item in obj) {
204      let property = obj[item];
205      console.info('key is ' + item);
206      console.info('value is ' + property);
207    }
208  }
209
210  async videoPlayerDemo() {
211    let videoPlayer = undefined;
212    let surfaceID = 'test' // surfaceID用于播放画面显示,具体的值需要通过Xcomponent接口获取,相关文档链接:
213    let fdPath = 'fd://'
214    // path路径的码流可通过"hdc file send D:\xxx\H264_AAC.mp4 /data/app/el1/bundle/public/ohos.acts.multimedia.video.videoplayer/ohos.acts.multimedia.video.videoplayer/assets/entry/resources/rawfile" 命令,将其推送到设备上
215    let path = '/data/app/el1/bundle/public/ohos.acts.multimedia.video.videoplayer/ohos.acts.multimedia.video.videoplayer/assets/entry/resources/rawfile/H264_AAC.mp4';
216    let file = await fs.open(path);
217    fdPath = fdPath + '' + file.fd;
218    // 调用createVideoPlayer接口返回videoPlayer实例对象
219    await media.createVideoPlayer().then((video) => {
220      if (typeof (video) != 'undefined') {
221        console.info('createVideoPlayer success!');
222        videoPlayer = video;
223      } else {
224        console.info('createVideoPlayer fail!');
225      }
226    }, this.failureCallback).catch(this.catchCallback);
227    // 设置播放源
228    videoPlayer.url = fdPath;
229
230    // 设置surfaceID用于显示视频画面
231    await videoPlayer.setDisplaySurface(surfaceID).then(() => {
232      console.info('setDisplaySurface success');
233    }, this.failureCallback).catch(this.catchCallback);
234
235    // 调用prepare完成播放前准备工作
236    await videoPlayer.prepare().then(() => {
237      console.info('prepare success');
238    }, this.failureCallback).catch(this.catchCallback);
239
240    // 调用play接口正式开始播放
241    await videoPlayer.play().then(() => {
242      console.info('play success');
243    }, this.failureCallback).catch(this.catchCallback);
244
245    // 结束播放
246    await videoPlayer.stop().then(() => {
247      console.info('stop success');
248    }, this.failureCallback).catch(this.catchCallback);
249
250    // 释放播放资源
251    await videoPlayer.release().then(() => {
252      console.info('release success');
253    }, this.failureCallback).catch(this.catchCallback);
254
255    // 相关对象置undefined
256    videoPlayer = undefined;
257    surfaceID = undefined;
258  }
259}
260```
261
262### 切视频场景
263
264```js
265import media from '@ohos.multimedia.media'
266import fs from '@ohos.file.fs'
267export class VideoPlayerDemo {
268  // 函数调用发生错误时用于上报错误信息
269  failureCallback(error) {
270    console.info(`error happened,error Name is ${error.name}`);
271    console.info(`error happened,error Code is ${error.code}`);
272    console.info(`error happened,error Message is ${error.message}`);
273  }
274
275  // 当函数调用发生异常时用于上报错误信息
276  catchCallback(error) {
277    console.info(`catch error happened,error Name is ${error.name}`);
278    console.info(`catch error happened,error Code is ${error.code}`);
279    console.info(`catch error happened,error Message is ${error.message}`);
280  }
281
282  // 用于打印视频轨道信息
283  printfDescription(obj) {
284    for (let item in obj) {
285      let property = obj[item];
286      console.info('key is ' + item);
287      console.info('value is ' + property);
288    }
289  }
290
291  async videoPlayerDemo() {
292    let videoPlayer = undefined;
293    let surfaceID = 'test' // surfaceID用于播放画面显示,具体的值需要通过Xcomponent接口获取,相关文档链接:
294    let fdPath = 'fd://'
295    // path路径的码流可通过"hdc file send D:\xxx\H264_AAC.mp4 /data/app/el1/bundle/public/ohos.acts.multimedia.video.videoplayer/ohos.acts.multimedia.video.videoplayer/assets/entry/resources/rawfile" 命令,将其推送到设备上
296    let path = '/data/app/el1/bundle/public/ohos.acts.multimedia.video.videoplayer/ohos.acts.multimedia.video.videoplayer/assets/entry/resources/rawfile/H264_AAC.mp4';
297    let nextPath = '/data/app/el1/bundle/public/ohos.acts.multimedia.video.videoplayer/ohos.acts.multimedia.video.videoplayer/assets/entry/resources/rawfile/MP4_AAC.mp4';
298    let file = await fs.open(path);
299    fdPath = fdPath + '' + file.fd;
300    // 调用createVideoPlayer接口返回videoPlayer实例对象
301    await media.createVideoPlayer().then((video) => {
302      if (typeof (video) != 'undefined') {
303        console.info('createVideoPlayer success!');
304        videoPlayer = video;
305      } else {
306        console.info('createVideoPlayer fail!');
307      }
308    }, this.failureCallback).catch(this.catchCallback);
309    // 设置播放源
310    videoPlayer.url = fdPath;
311
312    // 设置surfaceID用于显示视频画面
313    await videoPlayer.setDisplaySurface(surfaceID).then(() => {
314      console.info('setDisplaySurface success');
315    }, this.failureCallback).catch(this.catchCallback);
316
317    // 调用prepare完成播放前准备工作
318    await videoPlayer.prepare().then(() => {
319      console.info('prepare success');
320    }, this.failureCallback).catch(this.catchCallback);
321
322    // 调用play接口正式开始播放
323    await videoPlayer.play().then(() => {
324      console.info('play success');
325    }, this.failureCallback).catch(this.catchCallback);
326
327    // 重置播放配置
328    await videoPlayer.reset().then(() => {
329      console.info('reset success');
330    }, this.failureCallback).catch(this.catchCallback);
331
332    // 获取下一个视频fd地址
333    fdPath = 'fd://'
334    let nextFile = await fs.open(nextPath);
335    fdPath = fdPath + '' + nextFile.fd;
336    // 设置第二个视频播放源
337    videoPlayer.url = fdPath;
338
339    // 调用prepare完成播放前准备工作
340    await videoPlayer.prepare().then(() => {
341      console.info('prepare success');
342    }, this.failureCallback).catch(this.catchCallback);
343
344    // 调用play接口正式开始播放
345    await videoPlayer.play().then(() => {
346      console.info('play success');
347    }, this.failureCallback).catch(this.catchCallback);
348
349    // 释放播放资源
350    await videoPlayer.release().then(() => {
351      console.info('release success');
352    }, this.failureCallback).catch(this.catchCallback);
353
354    // 相关对象置undefined
355    videoPlayer = undefined;
356    surfaceID = undefined;
357  }
358}
359```
360
361### 单个视频循环场景
362
363```js
364import media from '@ohos.multimedia.media'
365import fs from '@ohos.file.fs'
366export class VideoPlayerDemo {
367  // 函数调用发生错误时用于上报错误信息
368  failureCallback(error) {
369    console.info(`error happened,error Name is ${error.name}`);
370    console.info(`error happened,error Code is ${error.code}`);
371    console.info(`error happened,error Message is ${error.message}`);
372  }
373
374  // 当函数调用发生异常时用于上报错误信息
375  catchCallback(error) {
376    console.info(`catch error happened,error Name is ${error.name}`);
377    console.info(`catch error happened,error Code is ${error.code}`);
378    console.info(`catch error happened,error Message is ${error.message}`);
379  }
380
381  // 用于打印视频轨道信息
382  printfDescription(obj) {
383    for (let item in obj) {
384      let property = obj[item];
385      console.info('key is ' + item);
386      console.info('value is ' + property);
387    }
388  }
389
390  async videoPlayerDemo() {
391    let videoPlayer = undefined;
392    let surfaceID = 'test' // surfaceID用于播放画面显示,具体的值需要通过Xcomponent接口获取,相关文档链接:
393    let fdPath = 'fd://'
394    // path路径的码流可通过"hdc file send D:\xxx\H264_AAC.mp4 /data/app/el1/bundle/public/ohos.acts.multimedia.video.videoplayer/ohos.acts.multimedia.video.videoplayer/assets/entry/resources/rawfile" 命令,将其推送到设备上
395    let path = '/data/app/el1/bundle/public/ohos.acts.multimedia.video.videoplayer/ohos.acts.multimedia.video.videoplayer/assets/entry/resources/rawfile/H264_AAC.mp4';
396    let file = await fs.open(path);
397    fdPath = fdPath + '' + file.fd;
398    // 调用createVideoPlayer接口返回videoPlayer实例对象
399    await media.createVideoPlayer().then((video) => {
400      if (typeof (video) != 'undefined') {
401        console.info('createVideoPlayer success!');
402        videoPlayer = video;
403      } else {
404        console.info('createVideoPlayer fail!');
405      }
406    }, this.failureCallback).catch(this.catchCallback);
407    // 设置播放源
408    videoPlayer.url = fdPath;
409
410    // 设置surfaceID用于显示视频画面
411    await videoPlayer.setDisplaySurface(surfaceID).then(() => {
412      console.info('setDisplaySurface success');
413    }, this.failureCallback).catch(this.catchCallback);
414
415    // 调用prepare完成播放前准备工作
416    await videoPlayer.prepare().then(() => {
417      console.info('prepare success');
418    }, this.failureCallback).catch(this.catchCallback);
419    // 设置循环播放属性
420    videoPlayer.loop = true;
421    // 调用play接口正式开始循环播放
422    await videoPlayer.play().then(() => {
423      console.info('play success, loop value is ' + videoPlayer.loop);
424    }, this.failureCallback).catch(this.catchCallback);
425  }
426}
427```
428
429## 相关实例
430针对视频播放开发,有以下相关实例可供参考:
431
432- [`VideoPlayer:`视频播放(ArkTS)(API9)(Full SDK)](https://gitee.com/openharmony/applications_app_samples/tree/OpenHarmony-3.2-Release/media/VideoPlayer)
433- [视频播放器(ArkTS)(Full SDK)(API9)](https://gitee.com/openharmony/codelabs/tree/master/Media/VideoPlayerStage)