HarmonyOS 列表中可以播放视频,如果全屏的话有什么好的方案吗?用的是 xcomponent 作为 surface

HarmonyOS
2天前
浏览
收藏 0
回答 1
待解决
回答 1
按赞同
/
按时间
put_get

可以参考以下demo:

Index.ets

import { myVideoSourceDate, VideoSource } from './myVideoSourceData';
import { VideoComponent } from './VideoComponent';

@Entry
@Component
struct XCAvplayer {
  private data: myVideoSourceDate = new myVideoSourceDate([]);
  @State isLayoutFullScreen : boolean = false;
  @State fangDaIndex : number = -1;

  aboutToAppear(): void {
    let list: VideoSource[] = [
      new VideoSource("文案1", "video1.mp4"),
      new VideoSource("文案2", "video2.mp4"),
      new VideoSource("文案3", "video3.mp4"),
      new VideoSource("文案4", "video4.mp4")
    ];
    console.log("myAppliction is Appear")
    this.data = new myVideoSourceDate(list);
  }

  build() {
    Scroll() {
      Column() {
        List() {
          LazyForEach(this.data, (item: VideoSource,index : number) => {
            ListItem() {
              VideoComponent({item : item, isLayoutFullScreen : this.isLayoutFullScreen, index : index , fangDaIndex : this.fangDaIndex})
                .visibility(this.isLayoutFullScreen && this.fangDaIndex !== index ? Visibility.None : Visibility.Visible)
            }
          }, (item: string) => item)
        }.cachedCount(5).scrollBar(BarState.Off).edgeEffect(this.isLayoutFullScreen ? EdgeEffect.None : EdgeEffect.Spring)
      }
    }
    .edgeEffect(this.isLayoutFullScreen ? EdgeEffect.None : EdgeEffect.Spring)
    .width('100%')
  }
}

AVPlayerDemo.ets

import media from '@ohos.multimedia.media';
import {BusinessError} from '@ohos.base';
import { ifaa } from '@kit.OnlineAuthenticationKit';
import { common } from '@kit.AbilityKit';

export class AVPlayerDemo {
  private count: number = 0;
  private surfaceID: string = ''; // surfaceID用于播放画面显示,具体的值需要通过Xcomponent接口获取,相关文档链接见上面Xcomponent创建方法。
  private isSeek: boolean = true; // 用于区分模式是否支持seek操作。
  private avPlayer: media.AVPlayer | undefined = undefined;

  setSurfaceID(surface_id: string){
    console.log('setSurfaceID : ' + surface_id);
    this.surfaceID = surface_id;
  }
  // 注册avplayer回调函数。
  setAVPlayerCallback(avPlayer: media.AVPlayer) {
    // seek操作结果回调函数。
    avPlayer.on('seekDone', (seekDoneTime: number) => {
      console.info(`AVPlayer seek succeeded, seek time is ${seekDoneTime}`);
    })
    // error回调监听函数,当avplayer在操作过程中出现错误时,调用reset接口触发重置流程。
    avPlayer.on('error', (err: BusinessError) => {
      console.error(`Invoke avPlayer failed, code is ${err.code}, message is ${err.message}`);
      avPlayer.reset();
    })
    // 状态机变化回调函数。
    avPlayer.on('stateChange', async (state: string, reason: media.StateChangeReason) => {
      switch (state) {
        case 'idle': // 成功调用reset接口后触发该状态机上报。
          console.info('AVPlayer state idle called.');
          avPlayer.release(); // 调用release接口销毁实例对象。
          break;
        case 'initialized': // avplayer 设置播放源后触发该状态上报。
          console.info('AVPlayer state initialized called.');
          avPlayer.surfaceId = this.surfaceID; // 设置显示画面,当播放的资源为纯音频时无需设置。
          avPlayer.prepare();
          break;
        case 'prepared': // prepared调用成功后上报该状态机。
          console.info('AVPlayer state prepared called.');
        // if(this.count === 0) {
        // console.info('AVPlayer start to play.');
        // avPlayer.play(); // 调用播放接口开始播放。
        // } else if(this.count === 1) {
        // avPlayer.surfaceId = this.surfaceID;
        // console.info('AVPlayer start to seek.');
        // avPlayer.seek(avPlayer.currentTime); // seek到视频末尾。
        // avPlayer.play();
        // }
          avPlayer.play();
          break;
        case 'playing': // play成功调用后触发该状态机上报。
          console.info('AVPlayer state playing called.');
        // if(this.count === 0) {
        // console.info('AVPlayer start to play.');
        // } else if(this.count === 1) {
        // avPlayer.surfaceId = this.surfaceID;
        // console.info('AVPlayer start to seek.');
        // avPlayer.seek(avPlayer.currentTime); // seek到视频末尾。
        // }
        // if(this.count !== 0) {
        // avPlayer.surfaceId = this.surfaceID;
        // if (this.isSeek) {
        // console.info('AVPlayer start to seek.');
        // avPlayer.seek(avPlayer.currentTime); // seek到视频末尾。
        // } else {
        // // 当播放模式不支持seek操作时继续播放到结尾。
        // console.info('AVPlayer wait to play end.');
        // }
        // } else {
        // avPlayer.pause(); // 调用暂停接口暂停播放。
        // }
        // this.count++;
          break;
        case 'paused': // pause成功调用后触发该状态机上报。
          console.info('AVPlayer state paused called.');
        // avPlayer.play(); // 再次播放接口开始播放。
          break;
        case 'completed': //播放接口后触发该状态机上报。
          console.info('AVPlayer state paused called.');
          avPlayer.play(); // 调用播放接口接口。
          break;
        case 'stopped': // stop接口后触发该状态机上报。
          console.info('AVPlayer state stopped called.');
          avPlayer.reset(); // 调用reset接口初始化avplayer状态。
          break;
        case 'released': //播放接口后触发该状态机上报。
          console.info('AVPlayer state released called.');
          break;
        default:
          break;
      }
    })
  }

  // 通过url设置网络地址来实现播放直播码流。
  async avPlayerLiveDemo(count : number ,url: string){
    this.count = count
    // async avPlayerLiveDemo(){
    // 创建avPlayer实例对象
    this.avPlayer = await media.createAVPlayer();
    // 创建状态机变化回调函数。
    this.setAVPlayerCallback(this.avPlayer);
    // this.isSeek = false; // 不支持seek操作。
    // if (this.count !== 0) {
    //
    // this.isSeek = true; // 不支持seek操作。
    // }
    let context = getContext(this) as common.UIAbilityContext;
    let fileDescriptor = await context.resourceManager.getRawFd(url);
    let avFileDescriptor: media.AVFileDescriptor =
      { fd: fileDescriptor.fd, offset: fileDescriptor.offset, length: fileDescriptor.length };
    this.avPlayer.fdSrc = avFileDescriptor;
    // avPlayer.url = "https://videocdn.bodybuilding.com/video/mp4/62000/62792m.mp4";
  }

  async release() {
    this.avPlayer?.reset();
  }

  getStage(): string {
    if (this.avPlayer !== undefined) {
      return this.avPlayer.state;
    }
    return 'undefined';
  }
}

myVideoSourceData.ets

export class myVideoSourceDate implements IDataSource {
  videoList: VideoSource[] = [];

  constructor(videoList: VideoSource[]) {
    this.videoList = videoList;
  }

  totalCount(): number {
    return this.videoList.length;
  }

  getData(index: number): VideoSource {
    return this.videoList[index];
  }

  registerDataChangeListener(listener: DataChangeListener): void {
  }

  unregisterDataChangeListener(listener: DataChangeListener): void {
  }
}

@Observed
export class VideoSource {
  text: string;
  url: string;

  constructor(text: string,url: string) {
    this.text = text;
    this.url = url;
  }
}

VideoComponent.ets

import { AVPlayerDemo } from './AVPlayerDemo';
import { VideoSource } from './myVideoSourceData';
import window from '@ohos.window';
import { BusinessError } from '@ohos.base';
import { router } from '@kit.ArkUI';

let isPlaying: AVPlayerDemo[] = [];

@Component
export struct VideoComponent {
  @ObjectLink item: VideoSource;
  index : number = -1;
  @Link isLayoutFullScreen: boolean;
  @Link fangDaIndex : number;
  @State bkColor: Color = Color.Red
  mXComponentController: XComponentController = new XComponentController();
  @State player_changed: boolean = false;
  @State isLandScape: boolean = false;
  player?: AVPlayerDemo;

  // 设置窗口方向
  setR(orientation: number) {
    window.getLastWindow(getContext(this)).then((win) => {
      win.setPreferredOrientation(orientation).then((data) => {
        console.log('setWindowOrientation: ' + orientation + ' Succeeded. Data: ' + JSON.stringify(data));
      }).catch((err: string) => {
        console.log('setWindowOrientation: Failed. Cause: ' + JSON.stringify(err));
      });
    }).catch((err: string) => {
      console.log('setWindowOrientation: Failed to obtain the top window. Cause: ' + JSON.stringify(err));
    });
  }

  //设置沉浸式窗口
  setFullScreen(isLayoutFullScreen: boolean) {
    window.getLastWindow(getContext(this)).then((win) => {
      win.setWindowLayoutFullScreen(isLayoutFullScreen, (err: BusinessError) => {
        const errCode: number = err.code;
        if (errCode) {
          console.error('Failed to set the window layout to full-screen mode. Cause:' + JSON.stringify(err));
          return;
        }
        console.info('Succeeded in setting the window layout to full-screen mode.');
      });
    }).catch((err: string) => {
      console.log('setWindowOrientation: Failed to obtain the top window. Cause: ' + JSON.stringify(err));
    });
  }

  build() {
    //通过显隐控制控制其他listItem是否展示
    Column() {
      Text(this.item.text)
        .visibility(this.isLayoutFullScreen === false ? Visibility.Visible : Visibility.None)
      Stack() {
        XComponent({ id: 'video_player_id', type: XComponentType.SURFACE, controller: this.mXComponentController })
          .onLoad(() => {
            this.player = new AVPlayerDemo();
            this.player.setSurfaceID(this.mXComponentController.getXComponentSurfaceId());
            // this.player_changed = !this.player_changed;
            // this.player.avPlayerLiveDemo(0, this.item.url)
          })
          .height(this.isLayoutFullScreen ? (this.isLandScape ? '100%' : 200) : "100%")
        Row() {
          Button(this.player && (this.player.getStage() === 'playing') ? '播放中' : '开始')
            .onClick(async () => {
              if (isPlaying.length !== 0) {
                let play = isPlaying.pop();
                await play?.release();
              }
              if (this.player) {
                this.player.avPlayerLiveDemo(0, this.item.url);
                isPlaying.push(this.player);
              }
            })
            .backgroundColor(this.bkColor)
          Button("点击全屏")
            .onClick(() => {
              this.fangDaIndex = this.index;
              this.isLayoutFullScreen = true;
              this.setFullScreen(this.isLayoutFullScreen)
            })
            .backgroundColor(this.bkColor)

          Button("退出全屏")
            .onClick(() => {
              this.setR(1);
              this.isLayoutFullScreen = false;
              this.isLandScape = false;
              this.setFullScreen(this.isLayoutFullScreen)
            })
            .backgroundColor(this.bkColor)

          Button("横屏")
            .onClick(() => {
              this.fangDaIndex = this.index;
              this.setR(4);
              this.isLandScape = true;
              this.isLayoutFullScreen = true;
              this.setFullScreen(this.isLayoutFullScreen)
            })
            .backgroundColor(this.bkColor)

          Button("退出横屏")
            .onClick(() => {
              this.setR(1);
              this.isLandScape = false;
            })
            .backgroundColor(this.bkColor)
        }
      }
      .backgroundColor(Color.Black)
      .height(this.isLayoutFullScreen ? "100%" : 200)
    }
    .onVisibleAreaChange([0.2, 1.0], async (isVisible: boolean, currentRatio: number) => {
      if (!isVisible && currentRatio < 0.2) {
        if (this.player && isPlaying.length !== 0 && this.player === isPlaying[0]) {
          console.info('onVisibleAreaChange')
          this.player.release();
          isPlaying[0].release();
          isPlaying.pop();
        }
      }

      // if (isVisible && currentRatio >= 1.0) {
      // this.isSlide = true;
      // }
    })
    .width('100%')
  }
}
分享
微博
QQ
微信
回复
2天前
相关问题
HarmonyOS 列表视频全屏播放实现
449浏览 • 1回复 待解决
在鸿蒙netty替代方案
65浏览 • 0回复 待解决
屏幕旋转计算,什么方案
951浏览 • 1回复 待解决
Text模拟隐私协议,方案
782浏览 • 1回复 待解决
连接网络信息获取方案
657浏览 • 1回复 待解决
har和hsp转换,方案
698浏览 • 1回复 待解决
HarmonyOS XComponent播放视频问题
32浏览 • 1回复 待解决
转场动画,谁有方案
606浏览 • 1回复 待解决