1# 视频播放开发指导 2 3## 简介 4 5视频播放的主要工作是将视频数据转码并输出到设备进行播放,同时管理播放任务,包括开始播放、暂停播放、停止播放、资源释放、音量设置、跳转播放位置、设置倍数、获取轨道信息等功能控制。本文将对视频播放全流程、视频切换、视频循环播放等场景开发进行介绍说明。 6 7## 运作机制 8 9该模块提供了视频播放状态变化示意图和视频播放外部模块交互图。 10 11**图1** 视频播放状态变化示意图 12 13 14 15**图2** 视频播放外部模块交互图 16 17 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)