
不接视频服务器怎样在 HarmonyOS 手机上把 3 张照片生成 6 秒回忆电影真机环境ALN-AL80OpenHarmony 7.0.0.32Beta2API 263 张输入图均为公开测试素材编码、配乐合成、播放和保存全部在手机本地完成。为什么相册里的照片需要“成片”出口旅行结束后相册里通常留下几十张照片。逐张翻看保留了细节但分享时缺少节奏把照片上传到服务器生成视频又会增加等待、流量和隐私成本。双镜记忆相机需要一条更短的路径选中几张照片确定时长与配乐在本机生成可播放的 MP4。用户可以先预览确认无误后再存入成片列表。网络中断不影响编码照片也不需要离开设备。本次真机实践选择 3 张公开照片设置 6 秒时长和“晨光微风”配乐生成 720×1280 的竖屏 AVC 视频。取证覆盖选片、配置、生成、播放和保存不把静态设计稿当成视频结果。本地生成要解决哪些问题HarmonyOS 官方文档中心把视频编解码归入 AVCodec Kit把AVPlayer播放归入 Media Kit。前者负责把连续图像帧编码为视频后者负责生成结果的本地播放与状态控制。照片生成短片不是把 JPEG 文件简单拼接在一起至少包含五段处理选择照片 → 按时长分配帧 → 裁剪、缩放并转换为编码像素格式 → AVC 编码为 MP4 → 合并配乐并交给播放器预览输入照片横竖比例不同编码器又要求稳定的宽高、偶数尺寸和可接受的像素格式。任何一步处理不一致最终都可能表现为花屏、拉伸、编码失败或只有声音没有画面。第一步选择参与成片的照片页面先展示当前公开相册私密记录不会进入候选列表。普通短片至少需要两张照片本次依次选择旅行合成图、人物原图和城市夜景。图 1真机进入普通短片选片页公开照片以缩略图形式展示。选片结果保存的是相册记录对应的真实文件路径。开始生成前再次过滤空路径防止照片在页面停留期间被删除constnormalizedPathssourceImagePaths.filter(pathpath.trim().length0);if(normalizedPaths.length2){thrownewError(至少需要 2 张素材才能生成普通短片);}第二步把“6 秒”换算成真实视频帧生成器使用 2 fps。6 秒短片需要 12 帧三张照片按时间区间均匀分配每张图连续占用四帧。这里的低帧率适合静态照片轮播也能控制短片体积如果加入平移和缩放动画帧率要相应提高。constframeRate2;consttargetFrameCountMath.round(durationSeconds*frameRate);constframePaths:Arraystring[];for(letindex0;indextargetFrameCount;index){constsourceIndexMath.min(normalizedPaths.length-1,Math.floor(index*normalizedPaths.length/targetFrameCount));framePaths.push(normalizedPaths[sourceIndex]);}时长被限制在 330 秒防止误操作生成过长任务。生成按钮按下以后页面冻结当前选片和参数避免编码中途修改素材导致帧序列与 UI 不一致。本次配置为手动选片、6 秒、“晨光微风”配乐并在生成请求中打开智能降噪、空间音频、淡入淡出和宽阔声场。图 2页面显示已选 3 张时长、配乐和音频处理选项均已确定。截图能够证明这些参数进入了本次生成请求。降噪和空间音频的实际听感需要耳机与音频分析验证不能仅凭开关状态下结论本次结果确认的是配乐音轨已经写入并可随视频播放。第三步不同尺寸照片统一为 720×1280直接把横图缩放到竖屏会把人物和建筑拉长。生成器采用居中cover裁剪先根据源图与目标画布的宽高比计算裁剪区域再缩放到 720×1280。裁剪宽高和偏移都落在偶数像素边界满足 NV12 编码要求。constcropRegionMovieFramePolicy.buildCoverCropRegion({width:imageInfo.size.width,height:imageInfo.size.height},{width:720,height:1280});真机接入时遇到的关键问题发生在解码阶段API 26 上JPEG 解码同时请求“裁剪 缩放 NV12”会失败。稳定路径是先按文档支持良好的RGBA_8888完成裁剪和缩放再把PixelMap转成 NV12。constpixelMapawaitimageSource.createPixelMap({editable:true,desiredPixelFormat:image.PixelMapFormat.RGBA_8888,desiredSize:{width:720,height:1280},desiredRegion:cropRegion,cropAndScaleStrategy:image.CropAndScaleStrategy.CROP_FIRST});awaitpixelMap.convertPixelFormat(image.PixelMapFormat.NV12);这个顺序多了一次像素格式转换却把 JPEG 解码和视频编码的职责分开错误也更容易定位。全部帧进入编码队列后PixelMap会集中释放避免 12 帧同时长期占用图像内存。第四步编码 AVC 并合并本地配乐当前输出规格为 720×1280、2 fps、12 Mbps、video/avc。ArkTS 把准备好的 NV12PixelMap推给本地原生编码层再传入输出文件描述符、画面尺寸、帧率和帧数。transcoding.startTransCode(outputFd,720,1280,video/avc,2,12*1024*1024,frameCount,onFinished);编码先写入临时无声 MP4。选择配乐后本地合成层把应用资源中的 M4A 音轨写入最终文件合并成功才删除临时文件。自定义音乐无法写入时直接提示格式问题内置配乐失败则保留可播放的无声版本避免整段视频一起丢失。constresultawaittranscoding.mergeMovieAudioEnhanced(stagedVideoPath,musicPath,finalVideoPath,durationUs,noiseReduction,spatialAudio,soundFieldType,fadeEnabled?0.8:0,fadeEnabled?1.2:0);第五步用真实播放器完成结果闭环编码结束后页面进入新生成的短片预览。画面显示 6 秒时长、三张素材缩略图和“晨光微风”配乐底部“存入成片”按钮此时才可用。图 3真机生成结果页显示 00:06、3 张照片和配乐名称视频文件已经可以预览。预览音频由AVPlayer读取本地文件描述符。播放器设置为音乐用途准备完成后响应播放、暂停和页面离开时的释放constfilefs.openSync(videoPath,fs.OpenMode.READ_ONLY);player.fdSrc{fd:file.fd,offset:0,length:fs.statSync(videoPath).size};awaitpreparePlayer(player);// 将 callback 形式的 prepare 封装为 Promiseawaitplayer.play();图 4播放进度已经从 00:00 前进说明页面展示的是可播放 MP4而不是一张静态封面。确认预览后点击“存入成片”视频记录写入本地成片列表。列表从 0 条变为 1 条并显示封面、00:06、三张照片和配乐名称。图 5短片已经保存列表状态为“已保存”完整链路到此结束。这条链路为什么没有接服务器照片解码、画幅处理、视频编码、音轨合并和播放都使用本地文件。网络不参与生成过程公开测试照片没有上传。带来的直接收益有三点选择照片后可以立刻生成失败原因集中在本地格式和资源状态私密照片无需跨越网络边界但业务仍要在选片入口过滤访问权限生成文件可以直接进入应用的本地成片列表再由用户决定是否分享。本地处理也要控制资源。720×1280 的多帧PixelMap会快速占用内存长视频不能一次把全部帧常驻内存当前 330 秒的照片短片适合批量准备继续扩展时应改成生产者—消费者式逐帧送入编码器。最终效果这次真机实践完成了可复查的完整结果三张公开照片被选中按 6 秒和 2 fps 生成 12 帧统一裁剪为 720×1280转换为 NV12 后编码成 AVC随后写入“晨光微风”配乐。生成结果能够在真机播放进度正常前进并成功保存到成片列表。最关键的工程修正是把 JPEG 的 RGBA 解码与 NV12 编码输入拆成两步。解决这个格式问题后照片成片不再依赖云端接口用户从选片到得到可播放 MP4 只需要一次本地流程。