HarmonyOS鸿蒙Next中怎样实现组件录制

HarmonyOS鸿蒙Next中怎样实现组件录制 cke_239.png

目前我已知使用UIContext中的getComponentSnapshot()方法中的createFromBuilder可以实现离线构建组件然后对该组件截图。但是我的组件可能是动态渲染了,我想要对我的组件进行一定时间一定帧率的录制,怎样实现?


更多关于HarmonyOS鸿蒙Next中怎样实现组件录制的实战教程也可以访问 https://www.itying.com/category-93-b0.html

5 回复
  • getComponentSnapshot:获取ComponentSnapshot对象,可通过该对象获取组件截图的能力。典型使用场景(如长截图)及最佳实践请参考使用组件截图
  • setInterval:重复调用一个函数,在每次调用之间具有固定的时间延迟。
  • setTimeout:设置一个定时器,该定时器在定时器到期后执行一个函数。
import { image } from '@kit.ImageKit';
import { UIContext } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';

const FPS = 10;
const DURATION = 5000; // 5秒

@Entry
@Component
struct Index {
  @State changeText: number = 1;
  uiContext: UIContext = this.getUIContext();
  intervalId: number = -1;
  @State frameArray: Array<image.PixelMap> = [];

  aboutToAppear(): void {
    setInterval(() => {
      this.changeText++;
    }, 500);
  }

  @Builder
  RandomBuilder() {
    Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
      Divider().height(10);
      Text(`改变的文本 ${this.changeText}`)
        .fontSize(20)
        .width(100)
        .height(50)
        .textAlign(TextAlign.Center);
      Divider().height(10);
    }
    .width(100)
    .id('DYNAMIC_COMPONENT_ID');
  }

  build() {
    Column({ space: 20 }) {
      Button('录制5秒,图片保存在数组中')
        .onClick(() => {
          this.intervalId = setInterval(() => {
            this.uiContext.getComponentSnapshot()
              .createFromBuilder(() => {
                this.RandomBuilder();
              }, 0, false, { scale: 2, waitUntilRenderFinished: true })
              .then((pixmap: image.PixelMap) => {
                this.frameArray.push(pixmap);
              })
              .catch((err: BusinessError) => {
                console.error(`error: ${err.code}, message: ${err.message}.`);
              });
          }, 1000 / FPS); // 10帧率录制
          setTimeout(() => {
            clearInterval(this.intervalId);
          }, DURATION); // 5秒后停止录制
        });

      Scroll() {
        Column() {
          ForEach(this.frameArray, (item: PixelMap, index: number) => {
            Image(item)
              .height(200)
              .width(200);
          });
        };
      };
    }.width('100%').margin({ left: 10, top: 5, bottom: 5 }).height(300);
  }
}

更多关于HarmonyOS鸿蒙Next中怎样实现组件录制的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


参考 使用组件截图(ComponentSnapshot)-UI系统场景化能力-UI开发 (ArkTS声明式开发范式)

尽管截图只需传入一个组件根节点即可实现对其下所有组件进行截图,但当子组件中存在VideoXComponentWeb组件时,这并不是推荐的截图方式。建议直接使用image.createPixelMapFromSurface接口来实现。

例如:

import { BusinessError } from '@kit.BasicServicesKit';

async function CreatePixelMapFromSurface(surfaceId: string) {
  let region: image.Region = { x: 0, y: 0, size: { height: 100, width: 100 } };
  image.createPixelMapFromSurface(surfaceId, region).then(() => {
    console.info('Succeeded in creating pixelmap from Surface');
  }).catch((error: BusinessError) => {
    console.error(`Failed to create pixelmap. code is ${error.code}, message is ${error.message}`);
  });
}

一定时间一定帧率的录制等于录视频了,多调用几次接口就行了。

或者使用 基于AVScreenCapture实现屏幕录制-音频和视频

有没有示例呀,

在HarmonyOS鸿蒙Next中,实现组件录制功能主要使用XComponentMediaRecorder
首先,通过XComponentsurfaceId获取录制表面。
然后,配置MediaRecorder实例,设置视频源为VideoSource.SURFACE_ID并关联上述surfaceId,同时配置输出格式、编码器等参数。
最后,调用prepare()start()开始录制。
录制结束后,调用stop()release()停止并释放资源。

在HarmonyOS Next中,要实现组件的动态录制(即一段时间内按帧率捕获组件状态),目前没有直接提供单次调用即可完成录制的API。不过,你可以通过结合定时器和getComponentSnapshot()方法,手动实现一个录制循环。

核心思路是:在指定的录制时长内,以设定的帧率间隔,反复调用getComponentSnapshot()来捕获组件每一帧的快照,并将这些快照(image.PixelMap对象)按顺序保存到一个数组中。录制结束后,你可以对这个数组进行处理,例如将其编码为视频文件或动态图像(如GIF)。需要注意的是,系统并未内置将PixelMap序列直接合成为视频的API,这部分编码工作通常需要借助第三方库或Native能力(如C++库)在应用侧完成。

以下是基于ArkTS实现录制循环的关键步骤代码示例:

import { UIContext, image } from '@kit.ArkUI';

// 假设你的动态组件通过Builder函数构建
@Builder
function MyDynamicComponent() {
  // 你的动态组件内容
}

class ComponentRecorder {
  private frameInterval: number = 0; // 每帧间隔(ms)
  private duration: number = 0; // 总录制时长(ms)
  private frames: image.PixelMap[] = []; // 存储捕获的帧
  private timerId: number | null = null;

  // 开始录制
  startRecording(uiContext: UIContext, targetBuilder: () => void, fps: number, recordSeconds: number) {
    this.frameInterval = 1000 / fps; // 根据帧率计算间隔
    this.duration = recordSeconds * 1000;
    this.frames = [];

    let elapsedTime = 0;
    const captureFrame = () => {
      // 使用createFromBuilder捕获当前帧
      uiContext.getComponentSnapshot().createFromBuilder(targetBuilder, (err, pixelMap) => {
        if (!err && pixelMap) {
          this.frames.push(pixelMap);
        }
      });

      elapsedTime += this.frameInterval;
      if (elapsedTime < this.duration) {
        this.timerId = setTimeout(captureFrame, this.frameInterval);
      } else {
        this.onRecordingComplete(); // 录制完成回调
      }
    };

    // 启动第一次捕获
    this.timerId = setTimeout(captureFrame, this.frameInterval);
  }

  // 停止录制
  stopRecording() {
    if (this.timerId) {
      clearTimeout(this.timerId);
      this.timerId = null;
      this.onRecordingComplete();
    }
  }

  private onRecordingComplete() {
    console.log(`录制结束,共捕获 ${this.frames.length} 帧`);
    // 此处:将 this.frames 中的 PixelMap 数组进行处理,例如编码为视频或GIF
    // 注意:视频编码需要额外实现或引入第三方能力。
  }

  // 获取已录制的帧(用于后续处理)
  getRecordedFrames(): image.PixelMap[] {
    return this.frames;
  }
}

使用示例:

let uiContext = getUIContext(); // 获取你的组件所在的UIContext
let recorder = new ComponentRecorder();

// 开始录制,例如30fps,录制5秒
recorder.startRecording(uiContext, MyDynamicComponent, 30, 5);

// 如需提前停止,可调用 recorder.stopRecording();

重要注意事项:

  1. 性能考虑:高频截图(如30fps)可能对性能造成压力,需在实际设备上充分测试,确保UI流畅性。
  2. 内存管理PixelMap对象占用内存较大,长时间或高分辨率录制可能导致内存快速增长。录制结束后应及时释放不再需要的PixelMap(调用其release()方法),并在编码完成后清空帧数组。
  3. 视频编码:如上所述,ArkTS API未提供直接将PixelMap序列合成视频的功能。你需要:
    • 考虑将每帧PixelMap编码为图片(如PNG、JPEG)后,使用Native(C++)层调用如FFmpeg等库来合成视频。
    • 或寻找适用于HarmonyOS的第三方Native视频编码库。
    • 如果输出为GIF等动态图格式,可能需要寻找相应的JavaScript/ArkTS编码库。
  4. 异步处理createFromBuilder是异步操作,需确保帧捕获顺序与间隔符合预期。

总结,你可以通过定时循环截图来模拟组件录制,但完整的视频文件生成需要额外的编码步骤来实现。

回到顶部