获取网络视频首帧图

方法一:需下载到本地(不推荐)


import { GetFirstFrameAnimation } from './GetFirstFrameAnimation';
import { media } from '@kit.MediaKit';
import { image } from '@kit.ImageKit';
import fs from '@ohos.file.fs';
import { common } from '@kit.AbilityKit';
import request from '@ohos.request';
import { rcp } from '@kit.RemoteCommunicationKit';
import { DownloadManager } from '@hadss/super_fast_file_trans';
import { DownloadConfig, DownloadListener, DownloadTask, DownloadProgressInfo } from '@hadss/super_fast_file_trans';


@Entry
@Component
struct Index {
  @State message: string = 'Hello World';
  @State coverImage: image.PixelMap | null = null;
  // pixelMap对象声明,用于图片显示
  @State pixelMap: image.PixelMap | string | undefined = undefined;
  private downloadTask: DownloadTask | undefined;
  private ctx: Context | undefined;

  // 获取视频第一帧
  async testFetchFrameByTime(filePath: string) {
    // 创建AVImageGenerator对象
    let avImageGenerator: media.AVImageGenerator = await media.createAVImageGenerator()
    let file = fs.openSync(filePath, fs.OpenMode.READ_ONLY);
    let avFileDescriptor: media.AVFileDescriptor = { fd: file.fd };
    avImageGenerator.fdSrc = avFileDescriptor;
    // 初始化入参
    let timeUs = 0
    let queryOption = media.AVImageQueryOptions.AV_IMAGE_QUERY_NEXT_SYNC
    let param: media.PixelMapParams = {
      width: 300,
      height: 400,
    }
    // 获取缩略图(promise模式)
    this.pixelMap = await avImageGenerator.fetchFrameByTime(timeUs, queryOption, param)
    // 释放资源(promise模式)
    avImageGenerator.release()
    console.info(`release success.`)
    fs.closeSync(file);
  }

  async aboutToAppear() {
    const videoUrl = 'http://192.168.1.254/DCIM/MOVIE/20260518165941_000001.MP4';

    const downloadManager = DownloadManager.getInstance(); // 获取DownloadManager单例对象

    this.ctx = getContext();

    await downloadManager.init(this.ctx); // 初始化数据库

    // 自定义下载回调
    let customDownloadListener: DownloadListener = {
      onSuccess: () => {
        console.log("onSuccess: download success");
        this.testFetchFrameByTime(this.ctx!.filesDir + '/aaa.mp4');
      },
      onFail: (err: BusinessError) => {
        console.error(`onFail: download fail, err.message:${err.message}, err.code:${err.code}`);
      },
      // ...
      onProgressUpdate: (downloadProgress: DownloadProgressInfo) => {
        console.log(`onProgressUpdate:, download: transferred size:${downloadProgress.transferredSize}, total size:${downloadProgress.totalSize}`);
        //如果下载超过1m则停止下载
        // if (downloadProgress.transferredSize > 1024 * 1024) {
        //   this.downloadTask?.pause();
        //   this.downloadTask?.cancel();
        //   // this.testFetchFrameByTime(this.ctx!.filesDir + '/aaa.mp4');
        // }
      },
    };

    // 自定义下载配置
    let downloadConfig: DownloadConfig = {
      url: `${videoUrl}`, // 远端下载地址(必选)
      fileName: `aaa.mp4`, // 下载后的本地文件名(必选)
      concurrency: 2, // 启用的并发线程数(可选)AppUtil.getApplicationContext().filesDir
      fileDir: this.ctx.filesDir, // 下载后的文件保存路径(可选)
      // ...
    };

    // 创建下载任务
    this.downloadTask = downloadManager.createDownloadTask(downloadConfig, customDownloadListener);
  }

  build() {
    Column() {
      Text('开始下载')
        .fontSize($r('app.float.page_text_font_size'))
        .fontWeight(FontWeight.Bold)
        .alignRules({
          center: { anchor: '__container__', align: VerticalAlign.Center },
          middle: { anchor: '__container__', align: HorizontalAlign.Center }
        })
        .onClick(() => {
          this.downloadTask?.start();
        })
      // Text(this.message)
      //   .id('HelloWorld')
      //   .fontSize($r('app.float.page_text_font_size'))
      //   .fontWeight(FontWeight.Bold)
      //   .alignRules({
      //     center: { anchor: '__container__', align: VerticalAlign.Center },
      //     middle: { anchor: '__container__', align: HorizontalAlign.Center }
      //   })
      //   .onClick(() => {
      //     this.message = 'Welcome';
      //   })
      // GetFirstFrameAnimation()

      Column() {
        if (this.pixelMap) {
          Image(this.pixelMap)
            .width(200)
            .height(200)
            .objectFit(ImageFit.Contain)
        }
      }

    }
    .height('100%')
    .width('100%')
  }
}

async function downloadAndExtractFrame(ctx: common.UIAbilityContext, url: string) {
  // const session = rcp.createSession();
  // let req = new rcp.Request("url", "POST");
  // session.fetch(req).then((response) => {
  //   console.info(`Succeeded in getting the response ${response}`);
  // }).catch((err: BusinessError) => {
  //   console.error(`err: error code is ${err.code}, error data is ${err.data}`);
  // });

  let headers: rcp.RequestHeaders = {
    "accept": "application/json"
  };
  let content = "data to send";
  let configuration: rcp.Configuration = {
    transfer: {
      timeout: { connectMs: 60000, transferMs: 60000 }
    }
  };
  let cookies: rcp.RequestCookies = { 'name1': 'value1', 'name2': 'value2' };
  let transferRange: rcp.TransferRange = { from: 0, to: 100 };
  let req = new rcp.Request(url, "POST", headers, content, cookies, transferRange, configuration);
}

方法二:直接获取(推荐)

import { media } from '@kit.MediaKit';
import { image } from '@kit.ImageKit';

@Entry
@Component
struct Index {
  @State message: string = 'Hello World';
  @State pixelMap: image.PixelMap | string | undefined = undefined;
  private videoUrl: string = '';
  @State videoWidth: number = 0;
  @State videoHeight: number = 0;

  aboutToAppear() {
    this.videoUrl = 'http://192.168.1.254/DCIM/MOVIE/20260520175143_000003.MP4';
  }

  build() {
    Column() {
      Text('开始下载')
        .fontSize($r('app.float.page_text_font_size'))
        .fontWeight(FontWeight.Bold)
        .alignRules({
          center: { anchor: '__container__', align: VerticalAlign.Center },
          middle: { anchor: '__container__', align: HorizontalAlign.Center }
        })
        .onClick(() => {
          getVideoKeyFramesWithInfo(this.videoUrl).then((res) => {
            if (res) {
              this.pixelMap = res.pixelMap;
              this.videoWidth = res.width;
              this.videoHeight = res.height;
            }
          });
        })

      Column() {
        if (this.pixelMap) {
          Image(this.pixelMap)
            .width(this.videoWidth)
            .height(this.videoHeight)
            .objectFit(ImageFit.Contain)
        }
      }

    }
    .height('100%')
    .width('100%')
  }
}


const TAG = "[KeyframeUtils]";

export interface VideoInfo {
  pixelMap: image.PixelMap;
  width: number;
  height: number;
}

// 获取网络视频关键帧,宽高单位px
export async function getVideoKeyFramesWithInfo(url: string): Promise<VideoInfo | null> {
  try {
    let avMetadataExtractor: media.AVMetadataExtractor = await media.createAVMetadataExtractor();
    let headers: Record<string, string> = {
      "User-Agent": "User-Agent-Value"
    };
    avMetadataExtractor.setUrlSource(url, headers);

    //获取视频宽高
    const metadata = await avMetadataExtractor.fetchMetadata();
    const width = Number(metadata.videoWidth) || 0;
    const height = Number(metadata.videoHeight) || 0;

    console.info(TAG, `Video frame size: ${width}x${height}`);

    let timeUs: number = 0; //关键帧位置
    let queryOption: media.AVImageQueryOptions = media.AVImageQueryOptions.AV_IMAGE_QUERY_PREVIOUS_SYNC;

    //图片宽高
    let param: media.PixelMapParams = {
      width: width,
      height: height
    }

    const pixelMap = await avMetadataExtractor.fetchFrameByTime(timeUs, queryOption, param);
    avMetadataExtractor.release();

    return pixelMap ? { pixelMap, width, height } : null;
  } catch (error) {
    console.error(TAG, `getVideoKeyFramesWithInfo error: ${JSON.stringify(error)}`);
    return null;
  }
}

posted @ 2026-05-20 15:54  带头大哥d小弟  阅读(4)  评论(0)    收藏  举报