video-playback.md 15.8 KB
Newer Older
W
wusongqing 已提交
1
# 视频播放开发指导
2

W
wusongqing 已提交
3
## 场景介绍
4

W
wusongqing 已提交
5
视频播放的主要工作是将视频数据转码并输出到设备进行播放,同时管理播放任务。本文将对视频播放全流程、视频切换、视频循环播放等场景开发进行介绍说明。
6

W
wusongqing 已提交
7
**图1** 视频播放状态机
8

W
wusongqing 已提交
9
![zh-ch_image_video_state_machine](figures/zh-ch_image_video_state_machine.png)
10

Z
zengyawen 已提交
11

12

W
wusongqing 已提交
13
**图2** 视频播放零层图
14

W
wusongqing 已提交
15
![zh-ch_image_video_player](figures/zh-ch_image_video_player.png)
16

W
wusongqing 已提交
17
*注意:视频播放需要显示、音频、编解码等硬件能力。
18

W
wusongqing 已提交
19 20 21
1. 三方应用从Xcomponent组件获取surfaceID。
2. 三方应用把surfaceID传递给VideoPlayer JS。
3. 媒体服务把帧数据flush给surface buffer。
22

W
wusongqing 已提交
23
## 开发步骤
24

W
wusongqing 已提交
25
详细API含义可参考:[js-apis-media.md](../reference/apis/js-apis-media.md)
26

W
wusongqing 已提交
27
### 全流程场景
28

W
wusongqing 已提交
29
包含流程:创建实例,设置url,设置SurfaceId,准备播放视频,播放视频,暂停播放,获取轨道信息,跳转播放位置,设置音量,设置倍速,结束播放,重置,释放资源等流程。
30

W
wusongqing 已提交
31
VideoPlayer支持的url媒体源输入类型可参考:[url属性说明](../reference/apis/js-apis-media.md#videoplayer_属性)
32

33 34
Xcomponent创建方法可参考:[Xcomponent创建方法](#Xcomponent创建方法)

35
```js
36 37 38
import media from '@ohos.multimedia.media'
import fileIO from '@ohos.fileio'

W
wusongqing 已提交
39 40
let videoPlayer = undefined; // 用于保存createVideoPlayer创建的对象
let surfaceID = undefined; // 用于保存Xcomponent接口返回的surfaceID
41

42 43 44 45 46 47
// 调用Xcomponent的接口用于获取surfaceID,并保存在surfaceID变量中,该接口由XComponent组件默认加载,非主动调用
LoadXcomponent() {
	surfaceID = this.$element('Xcomponent').getXComponentSurfaceId();
    console.info('LoadXcomponent surfaceID is' + surfaceID);
}

W
wusongqing 已提交
48
// 函数调用发生错误时用于上报错误信息
49 50 51 52 53 54
function failureCallback(error) {
    console.info(`error happened,error Name is ${error.name}`);
    console.info(`error happened,error Code is ${error.code}`);
    console.info(`error happened,error Message is ${error.message}`);
}

W
wusongqing 已提交
55
// 当函数调用发生异常时用于上报错误信息
56 57 58 59 60 61
function catchCallback(error) {
    console.info(`catch error happened,error Name is ${error.name}`);
    console.info(`catch error happened,error Code is ${error.code}`);
    console.info(`catch error happened,error Message is ${error.message}`);
}

W
wusongqing 已提交
62
// 用于打印视频轨道信息
63 64 65 66 67 68 69 70
function printfDescription(obj) {
    for (let item in obj) {
        let property = obj[item];
        console.info('key is ' + item);
        console.info('value is ' + property);
    }
}

W
wusongqing 已提交
71
// 调用createVideoPlayer接口返回videoPlayer实例对象
72 73 74 75 76 77 78 79 80
await media.createVideoPlayer().then((video) => {
    if (typeof (video) != 'undefined') {
        console.info('createVideoPlayer success!');
        videoPlayer = video;
    } else {
        console.info('createVideoPlayer fail!');
    }
}, failureCallback).catch(catchCallback);

81 82 83 84 85 86 87 88 89 90 91
// 用户选择视频设置fd(本地播放)
let fdPath = 'fd://'
let path = 'data/accounts/account_0/appdata/ohos.xxx.xxx.xxx/01.mp4';
await fileIO.open(path).then(fdNumber) => {
   fdPath = fdPath + '' + fdNumber;
   console.info('open fd sucess fd is' + fdPath);
}, (err) => {
   console.info('open fd failed err is' + err);
}),catch((err) => {
   console.info('open fd failed err is' + err);
});
W
wusongqing 已提交
92

93
videoPlayer.url = fdPath;
W
wusongqing 已提交
94 95

// 设置surfaceID用于显示视频画面
96 97 98 99
await videoPlayer.setDisplaySurface(surfaceID).then(() => {
    console.info('setDisplaySurface success');
}, failureCallback).catch(catchCallback);

W
wusongqing 已提交
100
// 调用prepare完成播放前准备工作
101 102 103 104
await videoPlayer.prepare().then(() => {
    console.info('prepare success');
}, failureCallback).catch(catchCallback);

W
wusongqing 已提交
105
// 调用play接口正式开始播放
106 107 108 109
await videoPlayer.play().then(() => {
    console.info('play success');
}, failureCallback).catch(catchCallback);

W
wusongqing 已提交
110
// 暂停播放
111 112 113 114
await videoPlayer.pause().then(() => {
    console.info('pause success');
}, failureCallback).catch(catchCallback);

W
wusongqing 已提交
115
// 通过promise回调方式获取视频轨道信息
116 117 118 119 120 121 122 123 124 125 126 127 128
let arrayDescription;
await videoPlayer.getTrackDescription().then((arrlist) => {
    if (typeof (arrlist) != 'undefined') {
        arrayDescription = arrlist;
    } else {
        console.log('video getTrackDescription fail');
    }
}, failureCallback).catch(catchCallback);

for (let i = 0; i < arrayDescription.length; i++) {
    printfDescription(arrayDescription[i]);
}

W
wusongqing 已提交
129
// 跳转播放时间到50s位置,具体入参意义请参考接口文档
130
let seekTime = 50000;
131
await videoPlayer.seek(seekTime, media.SeekMode._NEXT_SYNC).then((seekDoneTime) => {
132 133 134
    console.info('seek success');
}, failureCallback).catch(catchCallback);

W
wusongqing 已提交
135
// 音量设置接口,具体入参意义请参考接口文档
136 137 138 139 140
let volume = 0.5;
await videoPlayer.setVolume(volume).then(() => {
    console.info('setVolume success');
}, failureCallback).catch(catchCallback);

W
wusongqing 已提交
141
// 倍速设置接口,具体入参意义请参考接口文档
142
let speed = media.PlaybackRateMode.SPEED_FORWARD_2_00_X;
143 144 145 146
await videoPlayer.setSpeed(speed).then(() => {
    console.info('setSpeed success');
}, failureCallback).catch(catchCallback);

W
wusongqing 已提交
147
// 结束播放
148 149 150 151
await videoPlayer.stop().then(() => {
    console.info('stop success');
}, failureCallback).catch(catchCallback);

W
wusongqing 已提交
152
// 重置播放配置
153 154 155 156
await videoPlayer.reset().then(() => {
    console.info('reset success');
}, failureCallback).catch(catchCallback);

W
wusongqing 已提交
157
// 释放播放资源
158 159 160 161
await videoPlayer.release().then(() => {
    console.info('release success');
}, failureCallback).catch(catchCallback);

W
wusongqing 已提交
162
// 相关对象置undefined
163 164 165 166
videoPlayer = undefined;
surfaceID = undefined;
```

W
wusongqing 已提交
167
### 正常播放场景
168 169

```js
170 171 172
import media from '@ohos.multimedia.media'
import fileIO from '@ohos.fileio'

W
wusongqing 已提交
173 174
let videoPlayer = undefined; // 用于保存createVideoPlayer创建的对象
let surfaceID = undefined; // 用于保存Xcomponent接口返回的surfaceID
175

176 177 178 179 180 181
// 调用Xcomponent的接口用于获取surfaceID,并保存在surfaceID变量中,该接口由XComponent组件默认加载,非主动调用
LoadXcomponent() {
	surfaceID = this.$element('Xcomponent').getXComponentSurfaceId();
    console.info('LoadXcomponent surfaceID is' + surfaceID);
}

W
wusongqing 已提交
182
// 函数调用发生错误时用于上报错误信息
183 184 185 186 187 188
function failureCallback(error) {
    console.info(`error happened,error Name is ${error.name}`);
    console.info(`error happened,error Code is ${error.code}`);
    console.info(`error happened,error Message is ${error.message}`);
}

W
wusongqing 已提交
189
// 当函数调用发生异常时用于上报错误信息
190 191 192 193 194 195
function catchCallback(error) {
    console.info(`catch error happened,error Name is ${error.name}`);
    console.info(`catch error happened,error Code is ${error.code}`);
    console.info(`catch error happened,error Message is ${error.message}`);
}

W
wusongqing 已提交
196
// 设置'playbackCompleted'事件回调,播放完成触发
197 198 199 200 201 202 203 204 205 206 207 208 209
function SetCallBack(videoPlayer) {
	videoPlayer.on('playbackCompleted', () => {
        console.info('video play finish');
        
        await videoPlayer.release().then(() => {
    		console.info('release success');
		}, failureCallback).catch(catchCallback);

		videoPlayer = undefined;
        surfaceID = undefined;
    });
}

W
wusongqing 已提交
210
// 调用createVideoPlayer接口返回videoPlayer实例对象
211 212 213 214 215 216 217 218 219
await media.createVideoPlayer().then((video) => {
    if (typeof (video) != 'undefined') {
        console.info('createVideoPlayer success!');
        videoPlayer = video;
    } else {
        console.info('createVideoPlayer fail!');
    }
}, failureCallback).catch(catchCallback);

W
wusongqing 已提交
220
// 设置事件回调
221 222
SetCallBack(videoPlayer);

223 224 225 226 227 228 229 230 231 232 233
// 用户选择视频设置fd(本地播放)
let fdPath = 'fd://'
let path = 'data/accounts/account_0/appdata/ohos.xxx.xxx.xxx/01.mp4';
await fileIO.open(path).then(fdNumber) => {
   fdPath = fdPath + '' + fdNumber;
   console.info('open fd sucess fd is' + fdPath);
}, (err) => {
   console.info('open fd failed err is' + err);
}),catch((err) => {
   console.info('open fd failed err is' + err);
});
W
wusongqing 已提交
234

235
videoPlayer.url = fdPath;
W
wusongqing 已提交
236 237

// 设置surfaceID用于显示视频画面
238 239 240 241
await videoPlayer.setDisplaySurface(surfaceID).then(() => {
    console.info('setDisplaySurface success');
}, failureCallback).catch(catchCallback);

W
wusongqing 已提交
242
// 调用prepare完成播放前准备工作
243 244 245 246
await videoPlayer.prepare().then(() => {
    console.info('prepare success');
}, failureCallback).catch(catchCallback);

W
wusongqing 已提交
247
// 调用play接口正式开始播放
248 249 250 251 252
await videoPlayer.play().then(() => {
    console.info('play success');
}, failureCallback).catch(catchCallback);
```

W
wusongqing 已提交
253
### 切视频场景
254 255

```js
256 257 258
import media from '@ohos.multimedia.media'
import fileIO from '@ohos.fileio'

W
wusongqing 已提交
259 260
let videoPlayer = undefined; // 用于保存createVideoPlayer创建的对象
let surfaceID = undefined; // 用于保存Xcomponent接口返回的surfaceID
261

262 263 264 265 266 267
// 调用Xcomponent的接口用于获取surfaceID,并保存在surfaceID变量中,该接口由XComponent组件默认加载,非主动调用
LoadXcomponent() {
	surfaceID = this.$element('Xcomponent').getXComponentSurfaceId();
    console.info('LoadXcomponent surfaceID is' + surfaceID);
}

W
wusongqing 已提交
268
// 函数调用发生错误时用于上报错误信息
269 270 271 272 273 274
function failureCallback(error) {
    console.info(`error happened,error Name is ${error.name}`);
    console.info(`error happened,error Code is ${error.code}`);
    console.info(`error happened,error Message is ${error.message}`);
}

W
wusongqing 已提交
275
// 当函数调用发生异常时用于上报错误信息
276 277 278 279 280 281
function catchCallback(error) {
    console.info(`catch error happened,error Name is ${error.name}`);
    console.info(`catch error happened,error Code is ${error.code}`);
    console.info(`catch error happened,error Message is ${error.message}`);
}

W
wusongqing 已提交
282
// 设置'playbackCompleted'事件回调,播放完成触发
283 284 285 286 287 288 289 290 291 292 293 294 295
function SetCallBack(videoPlayer) {
	videoPlayer.on('playbackCompleted', () => {
        console.info('video play finish');
        
        await videoPlayer.release().then(() => {
    		console.info('release success');
		}, failureCallback).catch(catchCallback);

		videoPlayer = undefined;
        surfaceID = undefined;
    });
}

W
wusongqing 已提交
296
// 调用createVideoPlayer接口返回videoPlayer实例对象
297 298 299 300 301 302 303 304 305
await media.createVideoPlayer().then((video) => {
    if (typeof (video) != 'undefined') {
        console.info('createVideoPlayer success!');
        videoPlayer = video;
    } else {
        console.info('createVideoPlayer fail!');
    }
}, failureCallback).catch(catchCallback);

W
wusongqing 已提交
306
// 设置事件回调
307 308
SetCallBack(videoPlayer);

309 310 311 312 313 314 315 316 317 318 319
// 用户选择视频设置fd(本地播放)
let fdPath = 'fd://'
let path = 'data/accounts/account_0/appdata/ohos.xxx.xxx.xxx/01.mp4';
await fileIO.open(path).then(fdNumber) => {
   fdPath = fdPath + '' + fdNumber;
   console.info('open fd sucess fd is' + fdPath);
}, (err) => {
   console.info('open fd failed err is' + err);
}),catch((err) => {
   console.info('open fd failed err is' + err);
});
W
wusongqing 已提交
320

321
videoPlayer.url = fdPath;
W
wusongqing 已提交
322 323

// 设置surfaceID用于显示视频画面
324 325 326 327
await videoPlayer.setDisplaySurface(surfaceID).then(() => {
    console.info('setDisplaySurface success');
}, failureCallback).catch(catchCallback);

W
wusongqing 已提交
328
// 调用prepare完成播放前准备工作
329 330 331 332
await videoPlayer.prepare().then(() => {
    console.info('prepare success');
}, failureCallback).catch(catchCallback);

W
wusongqing 已提交
333
// 调用play接口正式开始播放
334 335 336 337
await videoPlayer.play().then(() => {
    console.info('play success');
}, failureCallback).catch(catchCallback);

W
wusongqing 已提交
338 339
// 播放一段时间后,下发切视频指令
// 重置播放配置
340 341 342 343
await videoPlayer.reset().then(() => {
    console.info('reset success');
}, failureCallback).catch(catchCallback);

344 345 346 347 348 349 350 351 352 353 354 355 356
// 用户选择视频设置fd(本地播放)
let fdNextPath = 'fd://'
let nextPath = 'data/accounts/account_0/appdata/ohos.xxx.xxx.xxx/02.mp4';
await fileIO.open(nextPath).then(fdNumber) => {
   fdNextPath = fdNextPath + '' + fdNumber;
   console.info('open fd sucess fd is' + fdNextPath);
}, (err) => {
   console.info('open fd failed err is' + err);
}),catch((err) => {
   console.info('open fd failed err is' + err);
});

videoPlayer.url = fdNextPath;
W
wusongqing 已提交
357 358

// 设置surfaceID用于显示视频画面
359 360 361 362
await videoPlayer.setDisplaySurface(surfaceID).then(() => {
    console.info('setDisplaySurface success');
}, failureCallback).catch(catchCallback);

W
wusongqing 已提交
363
// 调用prepare完成播放前准备工作
364 365 366 367
await videoPlayer.prepare().then(() => {
    console.info('prepare success');
}, failureCallback).catch(catchCallback);

W
wusongqing 已提交
368
// 调用play接口正式开始播放
369 370 371 372 373
await videoPlayer.play().then(() => {
    console.info('play success');
}, failureCallback).catch(catchCallback);
```

W
wusongqing 已提交
374
### 单个视频循环场景
375 376

```js
377 378 379
import media from '@ohos.multimedia.media'
import fileIO from '@ohos.fileio'

W
wusongqing 已提交
380 381
let videoPlayer = undefined; // 用于保存createVideoPlayer创建的对象
let surfaceID = undefined; // 用于保存Xcomponent接口返回的surfaceID
382

383 384 385 386 387 388
// 调用Xcomponent的接口用于获取surfaceID,并保存在surfaceID变量中,该接口由XComponent组件默认加载,非主动调用
LoadXcomponent() {
	surfaceID = this.$element('Xcomponent').getXComponentSurfaceId();
    console.info('LoadXcomponent surfaceID is' + surfaceID);
}

W
wusongqing 已提交
389
// 函数调用发生错误时用于上报错误信息
390 391 392 393 394 395
function failureCallback(error) {
    console.info(`error happened,error Name is ${error.name}`);
    console.info(`error happened,error Code is ${error.code}`);
    console.info(`error happened,error Message is ${error.message}`);
}

W
wusongqing 已提交
396
// 当函数调用发生异常时用于上报错误信息
397 398 399 400 401 402
function catchCallback(error) {
    console.info(`catch error happened,error Name is ${error.name}`);
    console.info(`catch error happened,error Code is ${error.code}`);
    console.info(`catch error happened,error Message is ${error.message}`);
}

W
wusongqing 已提交
403
// 设置'playbackCompleted'事件回调,播放完成触发
404 405 406 407 408 409 410 411 412 413 414 415 416
function SetCallBack(videoPlayer) {
	videoPlayer.on('playbackCompleted', () => {
        console.info('video play finish');
        
        await videoPlayer.release().then(() => {
    		console.info('release success');
		}, failureCallback).catch(catchCallback);

		videoPlayer = undefined;
        surfaceID = undefined;
    });
}

W
wusongqing 已提交
417
// 调用createVideoPlayer接口返回videoPlayer实例对象
418 419 420 421 422 423 424 425 426
await media.createVideoPlayer().then((video) => {
    if (typeof (video) != 'undefined') {
        console.info('createVideoPlayer success!');
        videoPlayer = video;
    } else {
        console.info('createVideoPlayer fail!');
    }
}, failureCallback).catch(catchCallback);

W
wusongqing 已提交
427
// 设置事件回调
428 429
SetCallBack(videoPlayer);

430 431 432 433 434 435 436 437 438 439 440
// 用户选择视频设置fd(本地播放)
let fdPath = 'fd://'
let path = 'data/accounts/account_0/appdata/ohos.xxx.xxx.xxx/01.mp4';
await fileIO.open(path).then(fdNumber) => {
   fdPath = fdPath + '' + fdNumber;
   console.info('open fd sucess fd is' + fdPath);
}, (err) => {
   console.info('open fd failed err is' + err);
}),catch((err) => {
   console.info('open fd failed err is' + err);
});
W
wusongqing 已提交
441

442
videoPlayer.url = fdPath;
W
wusongqing 已提交
443 444

// 设置surfaceID用于显示视频画面
445 446 447 448
await videoPlayer.setDisplaySurface(surfaceID).then(() => {
    console.info('setDisplaySurface success');
}, failureCallback).catch(catchCallback);

W
wusongqing 已提交
449
// 调用prepare完成播放前准备工作
450 451 452 453
await videoPlayer.prepare().then(() => {
    console.info('prepare success');
}, failureCallback).catch(catchCallback);

W
wusongqing 已提交
454
// 设置循环播放属性
455 456
videoPlayer.loop = true;

W
wusongqing 已提交
457
// 调用play接口正式开始播放
458 459 460
await videoPlayer.play().then(() => {
    console.info('play success');
}, failureCallback).catch(catchCallback);
461 462 463 464 465 466 467 468 469 470 471 472 473 474
```

### Xcomponent创建方法

播放视频中获取surfaceID依赖了Xcomponent,需要创建一个和xxx.js同名的xxx.hml文件,xxx.hml里面需要添加如下代码:

```js
<xcomponent id = 'Xcomponent'
	  if = "{{isFlush}}" // 刷新surfaceID,isFlush赋值false再赋值true为一次刷新,会主动再次加载LoadXcomponet获取新的surfaceID
      type = 'surface'
	  onload = 'LoadXcomponet' // 默认加载接口
      style = "wodth:720px;height:480px;border-color:red;border-width:5px;"> // 设置窗口宽高等属性
</xcomponent>
```