云计算百科
云计算领域专业知识百科平台

不接视频服务器:怎样在 HarmonyOS 手机上把 3 张照片生成 6 秒回忆电影

鸿蒙应用实操分享

不接视频服务器:怎样在 HarmonyOS 手机上把 3 张照片生成 6 秒回忆电影

真机环境:ALN-AL80,OpenHarmony 7.0.0.32(Beta2),API 26;3 张输入图均为公开测试素材,编码、配乐合成、播放和保存全部在手机本地完成。

为什么相册里的照片需要“成片”出口

旅行结束后,相册里通常留下几十张照片。逐张翻看保留了细节,但分享时缺少节奏;把照片上传到服务器生成视频,又会增加等待、流量和隐私成本。

双镜记忆相机需要一条更短的路径:选中几张照片,确定时长与配乐,在本机生成可播放的 MP4。用户可以先预览,确认无误后再存入成片列表。网络中断不影响编码,照片也不需要离开设备。

本次真机实践选择 3 张公开照片,设置 6 秒时长和“晨光微风”配乐,生成 720×1280 的竖屏 AVC 视频。取证覆盖选片、配置、生成、播放和保存,不把静态设计稿当成视频结果。

本地生成要解决哪些问题

HarmonyOS 官方文档中心把视频编解码归入 AVCodec Kit,把 AVPlayer 播放归入 Media Kit。前者负责把连续图像帧编码为视频,后者负责生成结果的本地播放与状态控制。

照片生成短片不是把 JPEG 文件简单拼接在一起,至少包含五段处理:

选择照片
→ 按时长分配帧
→ 裁剪、缩放并转换为编码像素格式
→ AVC 编码为 MP4
→ 合并配乐并交给播放器预览

输入照片横竖比例不同,编码器又要求稳定的宽高、偶数尺寸和可接受的像素格式。任何一步处理不一致,最终都可能表现为花屏、拉伸、编码失败或只有声音没有画面。

第一步:选择参与成片的照片

页面先展示当前公开相册,私密记录不会进入候选列表。普通短片至少需要两张照片;本次依次选择旅行合成图、人物原图和城市夜景。

回忆电影选片页面显示公开相册素材

图 1:真机进入普通短片选片页,公开照片以缩略图形式展示。

选片结果保存的是相册记录对应的真实文件路径。开始生成前再次过滤空路径,防止照片在页面停留期间被删除:

const normalizedPaths = sourceImagePaths.filter(
path => path.trim().length > 0
);
if (normalizedPaths.length < 2) {
throw new Error('至少需要 2 张素材才能生成普通短片');
}

第二步:把“6 秒”换算成真实视频帧

生成器使用 2 fps。6 秒短片需要 12 帧,三张照片按时间区间均匀分配,每张图连续占用四帧。这里的低帧率适合静态照片轮播,也能控制短片体积;如果加入平移和缩放动画,帧率要相应提高。

const frameRate = 2;
const targetFrameCount = Math.round(durationSeconds * frameRate);
const framePaths: Array<string> = [];

for (let index = 0; index < targetFrameCount; index++) {
const sourceIndex = Math.min(
normalizedPaths.length 1,
Math.floor(index * normalizedPaths.length / targetFrameCount)
);
framePaths.push(normalizedPaths[sourceIndex]);
}

时长被限制在 3~30 秒,防止误操作生成过长任务。生成按钮按下以后,页面冻结当前选片和参数,避免编码中途修改素材导致帧序列与 UI 不一致。

本次配置为手动选片、6 秒、“晨光微风”配乐,并在生成请求中打开智能降噪、空间音频、淡入淡出和宽阔声场。

回忆电影设置6秒配乐和音频选项

图 2:页面显示已选 3 张,时长、配乐和音频处理选项均已确定。

截图能够证明这些参数进入了本次生成请求。降噪和空间音频的实际听感需要耳机与音频分析验证,不能仅凭开关状态下结论;本次结果确认的是配乐音轨已经写入并可随视频播放。

第三步:不同尺寸照片统一为 720×1280

直接把横图缩放到竖屏会把人物和建筑拉长。生成器采用居中 cover 裁剪:先根据源图与目标画布的宽高比计算裁剪区域,再缩放到 720×1280。裁剪宽高和偏移都落在偶数像素边界,满足 NV12 编码要求。

const cropRegion = MovieFramePolicy.buildCoverCropRegion(
{ width: imageInfo.size.width, height: imageInfo.size.height },
{ width: 720, height: 1280 }
);

真机接入时遇到的关键问题发生在解码阶段:API 26 上,JPEG 解码同时请求“裁剪 + 缩放 + NV12”会失败。稳定路径是先按文档支持良好的 RGBA_8888 完成裁剪和缩放,再把 PixelMap 转成 NV12。

const pixelMap = await imageSource.createPixelMap({
editable: true,
desiredPixelFormat: image.PixelMapFormat.RGBA_8888,
desiredSize: { width: 720, height: 1280 },
desiredRegion: cropRegion,
cropAndScaleStrategy: image.CropAndScaleStrategy.CROP_FIRST
});

await pixelMap.convertPixelFormat(image.PixelMapFormat.NV12);

这个顺序多了一次像素格式转换,却把 JPEG 解码和视频编码的职责分开,错误也更容易定位。全部帧进入编码队列后,PixelMap 会集中释放,避免 12 帧同时长期占用图像内存。

第四步:编码 AVC 并合并本地配乐

当前输出规格为 720×1280、2 fps、12 Mbps、video/avc。ArkTS 把准备好的 NV12 PixelMap 推给本地原生编码层,再传入输出文件描述符、画面尺寸、帧率和帧数。

transcoding.startTransCode(
outputFd,
720,
1280,
'video/avc',
2,
12 * 1024 * 1024,
frameCount,
onFinished
);

编码先写入临时无声 MP4。选择配乐后,本地合成层把应用资源中的 M4A 音轨写入最终文件;合并成功才删除临时文件。自定义音乐无法写入时直接提示格式问题,内置配乐失败则保留可播放的无声版本,避免整段视频一起丢失。

const result = await transcoding.mergeMovieAudioEnhanced(
stagedVideoPath,
musicPath,
finalVideoPath,
durationUs,
noiseReduction,
spatialAudio,
soundFieldType,
fadeEnabled ? 0.8 : 0,
fadeEnabled ? 1.2 : 0
);

第五步:用真实播放器完成结果闭环

编码结束后,页面进入新生成的短片预览。画面显示 6 秒时长、三张素材缩略图和“晨光微风”配乐,底部“存入成片”按钮此时才可用。

六秒回忆电影已经生成并进入预览

图 3:真机生成结果页显示 00:06、3 张照片和配乐名称,视频文件已经可以预览。

预览音频由 AVPlayer 读取本地文件描述符。播放器设置为音乐用途,准备完成后响应播放、暂停和页面离开时的释放:

const file = fs.openSync(videoPath, fs.OpenMode.READ_ONLY);
player.fdSrc = {
fd: file.fd,
offset: 0,
length: fs.statSync(videoPath).size
};
await preparePlayer(player); // 将 callback 形式的 prepare 封装为 Promise
await player.play();

真机正在播放刚生成的回忆电影

图 4:播放进度已经从 00:00 前进,说明页面展示的是可播放 MP4,而不是一张静态封面。

确认预览后点击“存入成片”,视频记录写入本地成片列表。列表从 0 条变为 1 条,并显示封面、00:06、三张照片和配乐名称。

生成的六秒视频已保存到成片列表

图 5:短片已经保存,列表状态为“已保存”,完整链路到此结束。

这条链路为什么没有接服务器

照片解码、画幅处理、视频编码、音轨合并和播放都使用本地文件。网络不参与生成过程,公开测试照片没有上传。带来的直接收益有三点:

  • 选择照片后可以立刻生成,失败原因集中在本地格式和资源状态;
  • 私密照片无需跨越网络边界,但业务仍要在选片入口过滤访问权限;
  • 生成文件可以直接进入应用的本地成片列表,再由用户决定是否分享。

本地处理也要控制资源。720×1280 的多帧 PixelMap 会快速占用内存,长视频不能一次把全部帧常驻内存;当前 3~30 秒的照片短片适合批量准备,继续扩展时应改成生产者—消费者式逐帧送入编码器。

最终效果

这次真机实践完成了可复查的完整结果:三张公开照片被选中,按 6 秒和 2 fps 生成 12 帧,统一裁剪为 720×1280,转换为 NV12 后编码成 AVC,随后写入“晨光微风”配乐。生成结果能够在真机播放,进度正常前进,并成功保存到成片列表。

最关键的工程修正是把 JPEG 的 RGBA 解码与 NV12 编码输入拆成两步。解决这个格式问题后,照片成片不再依赖云端接口,用户从选片到得到可播放 MP4 只需要一次本地流程。

赞(0)
未经允许不得转载:网硕互联帮助中心 » 不接视频服务器:怎样在 HarmonyOS 手机上把 3 张照片生成 6 秒回忆电影
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!