【剪映小助手】音频处理接口

音频处理接口

目录

  1. 简介
  2. 项目结构
  3. 核心组件
  4. 架构总览
  5. 详细组件分析
  6. 依赖关系分析
  7. 性能考量
  8. 故障排查指南
  9. 结论
  10. 附录

简介

本文档介绍音频处理接口,聚焦以下两个核心能力:

  • 批量添加音频到剪映草稿的接口 /v1/add_audios,包括请求参数、音频信息数据结构、时间线布局、音量控制与音频效果应用
  • 音频时长获取接口 /v1/get_audio_duration 的使用方法与响应格式

文档提供最佳实践建议,包括音频同步、多轨混合与音频效果处理策略,并给出常见问题排查方法。

项目结构

音频处理的代码组织遵循"路由层 → 模型层 → 服务层 → 工具层"的分层设计,配合剪映草稿底层模型与轨道系统完成音频片段的创建与管理。
#mermaid-svg-CSTHGwoo2SxwAozt{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-CSTHGwoo2SxwAozt .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-CSTHGwoo2SxwAozt .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-CSTHGwoo2SxwAozt .error-icon{fill:#552222;}#mermaid-svg-CSTHGwoo2SxwAozt .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-CSTHGwoo2SxwAozt .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-CSTHGwoo2SxwAozt .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-CSTHGwoo2SxwAozt .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-CSTHGwoo2SxwAozt .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-CSTHGwoo2SxwAozt .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-CSTHGwoo2SxwAozt .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-CSTHGwoo2SxwAozt .marker{fill:#333333;stroke:#333333;}#mermaid-svg-CSTHGwoo2SxwAozt .marker.cross{stroke:#333333;}#mermaid-svg-CSTHGwoo2SxwAozt svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-CSTHGwoo2SxwAozt p{margin:0;}#mermaid-svg-CSTHGwoo2SxwAozt .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-CSTHGwoo2SxwAozt .cluster-label text{fill:#333;}#mermaid-svg-CSTHGwoo2SxwAozt .cluster-label span{color:#333;}#mermaid-svg-CSTHGwoo2SxwAozt .cluster-label span p{background-color:transparent;}#mermaid-svg-CSTHGwoo2SxwAozt .label text,#mermaid-svg-CSTHGwoo2SxwAozt span{fill:#333;color:#333;}#mermaid-svg-CSTHGwoo2SxwAozt .node rect,#mermaid-svg-CSTHGwoo2SxwAozt .node circle,#mermaid-svg-CSTHGwoo2SxwAozt .node ellipse,#mermaid-svg-CSTHGwoo2SxwAozt .node polygon,#mermaid-svg-CSTHGwoo2SxwAozt .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-CSTHGwoo2SxwAozt .rough-node .label text,#mermaid-svg-CSTHGwoo2SxwAozt .node .label text,#mermaid-svg-CSTHGwoo2SxwAozt .image-shape .label,#mermaid-svg-CSTHGwoo2SxwAozt .icon-shape .label{text-anchor:middle;}#mermaid-svg-CSTHGwoo2SxwAozt .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-CSTHGwoo2SxwAozt .rough-node .label,#mermaid-svg-CSTHGwoo2SxwAozt .node .label,#mermaid-svg-CSTHGwoo2SxwAozt .image-shape .label,#mermaid-svg-CSTHGwoo2SxwAozt .icon-shape .label{text-align:center;}#mermaid-svg-CSTHGwoo2SxwAozt .node.clickable{cursor:pointer;}#mermaid-svg-CSTHGwoo2SxwAozt .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-CSTHGwoo2SxwAozt .arrowheadPath{fill:#333333;}#mermaid-svg-CSTHGwoo2SxwAozt .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-CSTHGwoo2SxwAozt .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-CSTHGwoo2SxwAozt .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CSTHGwoo2SxwAozt .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-CSTHGwoo2SxwAozt .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CSTHGwoo2SxwAozt .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-CSTHGwoo2SxwAozt .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-CSTHGwoo2SxwAozt .cluster text{fill:#333;}#mermaid-svg-CSTHGwoo2SxwAozt .cluster span{color:#333;}#mermaid-svg-CSTHGwoo2SxwAozt div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-CSTHGwoo2SxwAozt .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-CSTHGwoo2SxwAozt rect.text{fill:none;stroke-width:0;}#mermaid-svg-CSTHGwoo2SxwAozt .icon-shape,#mermaid-svg-CSTHGwoo2SxwAozt .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CSTHGwoo2SxwAozt .icon-shape p,#mermaid-svg-CSTHGwoo2SxwAozt .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-CSTHGwoo2SxwAozt .icon-shape .label rect,#mermaid-svg-CSTHGwoo2SxwAozt .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CSTHGwoo2SxwAozt .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-CSTHGwoo2SxwAozt .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-CSTHGwoo2SxwAozt :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 工具与底层
服务层
模型层Pydantic
路由层
/v1/add_audios

POST
/v1/get_audio_duration

POST
AddAudiosRequest/Response
GetAudioDurationRequest/Response
add_audios(...)
get_audio_duration(...)
download(...)
get_media_duration(...)
AudioSegment / AudioEffect
Track / TrackType
config.py 常量
exceptions.py 错误码

核心组件

  • 路由接口
    • /v1/add_audios:接收草稿URL与音频信息JSON字符串,返回更新后的草稿URL、新增音频轨道ID与音频片段ID列表
    • /v1/get_audio_duration:接收音频URL,返回音频时长(微秒)
  • 数据模型
    • AddAudiosRequest/Response:定义请求与响应字段
    • GetAudioDurationRequest/Response:定义请求与响应字段
  • 服务逻辑
    • add_audios(...):解析音频信息、创建音频轨道、批量添加音频片段、应用音量与效果、保存草稿
    • get_audio_duration(...):下载音频、检测时长、清理临时文件
  • 工具与底层
    • download(...):通用下载器
    • get_media_duration(...):基于ffprobe的媒体时长检测
    • AudioSegment/AudioEffect:音频片段与效果对象
    • Track/TrackType:轨道类型与片段添加约束

架构总览

下图展示 /v1/add_audios 的端到端调用链路与关键处理步骤:
"剪映草稿模型(AudioSegment/Track)" "工具(download/get_media_duration)" "服务层(add_audios)" "模型层(Pydantic)" "路由层(v1)" "客户端" "剪映草稿模型(AudioSegment/Track)" "工具(download/get_media_duration)" "服务层(add_audios)" "模型层(Pydantic)" "路由层(v1)" "客户端" #mermaid-svg-jaXIbgCC4PKMuof6{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-jaXIbgCC4PKMuof6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-jaXIbgCC4PKMuof6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-jaXIbgCC4PKMuof6 .error-icon{fill:#552222;}#mermaid-svg-jaXIbgCC4PKMuof6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jaXIbgCC4PKMuof6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-jaXIbgCC4PKMuof6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jaXIbgCC4PKMuof6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jaXIbgCC4PKMuof6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-jaXIbgCC4PKMuof6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jaXIbgCC4PKMuof6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jaXIbgCC4PKMuof6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jaXIbgCC4PKMuof6 .marker.cross{stroke:#333333;}#mermaid-svg-jaXIbgCC4PKMuof6 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jaXIbgCC4PKMuof6 p{margin:0;}#mermaid-svg-jaXIbgCC4PKMuof6 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-jaXIbgCC4PKMuof6 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-jaXIbgCC4PKMuof6 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-jaXIbgCC4PKMuof6 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-jaXIbgCC4PKMuof6 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-jaXIbgCC4PKMuof6 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-jaXIbgCC4PKMuof6 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-jaXIbgCC4PKMuof6 .sequenceNumber{fill:white;}#mermaid-svg-jaXIbgCC4PKMuof6 #sequencenumber{fill:#333;}#mermaid-svg-jaXIbgCC4PKMuof6 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-jaXIbgCC4PKMuof6 .messageText{fill:#333;stroke:none;}#mermaid-svg-jaXIbgCC4PKMuof6 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-jaXIbgCC4PKMuof6 .labelText,#mermaid-svg-jaXIbgCC4PKMuof6 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-jaXIbgCC4PKMuof6 .loopText,#mermaid-svg-jaXIbgCC4PKMuof6 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-jaXIbgCC4PKMuof6 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-jaXIbgCC4PKMuof6 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-jaXIbgCC4PKMuof6 .noteText,#mermaid-svg-jaXIbgCC4PKMuof6 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-jaXIbgCC4PKMuof6 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-jaXIbgCC4PKMuof6 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-jaXIbgCC4PKMuof6 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-jaXIbgCC4PKMuof6 .actorPopupMenu{position:absolute;}#mermaid-svg-jaXIbgCC4PKMuof6 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-jaXIbgCC4PKMuof6 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-jaXIbgCC4PKMuof6 .actor-man circle,#mermaid-svg-jaXIbgCC4PKMuof6 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-jaXIbgCC4PKMuof6 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} POST /v1/add_audios 校验请求参数 调用 add_audios(draft_url, audio_infos) 解析/校验 audio_infos 下载音频文件 本地音频路径 检测音频时长 时长(微秒) 计算片段时间范围/音量/效果 创建音频轨道 创建 AudioSegment 并添加到轨道 保存草稿 返回 draft_url, track_id, audio_ids 200 OK 响应

详细组件分析

/v1/add_audios 接口

  • 接口定位
    • 批量向现有草稿添加音频素材,音频位于独立音频轨道,不影响视频内容
  • 请求参数
    • draft_url:目标草稿URL(必填)
    • audio_infos:JSON字符串数组,每项包含 audio_url、start、end、duration(可选)、volume(可选)、audio_effect(可选)
  • 响应参数
    • draft_url:更新后的草稿URL
    • track_id:新增音频轨道ID
    • audio_ids:本次添加的音频片段ID列表
  • 时间线布局与片段创建
    • 服务层会下载音频、检测实际时长,并根据 start/end 计算片段目标时间范围;若请求时长超过实际时长,将按实际时长截取;若不足则按请求时长截取但不超过实际时长
    • 片段音量通过 AudioSegment 的 volume 参数设置,默认1.0
    • 片段添加到新建的音频轨道上,轨道相对索引设置为10,避免与主轨道冲突
  • 音频效果
    • 支持通过 audio_effect 指定效果名称,服务层会查找对应效果类型并转换参数范围(0-100),然后应用到 AudioSegment
  • 错误处理
    • 草稿URL无效、audio_infos 缺失或格式错误、单条音频必填字段缺失、音量越界、时长非法、添加片段重叠等均会触发相应错误码

#mermaid-svg-Wh1KaYbdD8CllJj8{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Wh1KaYbdD8CllJj8 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Wh1KaYbdD8CllJj8 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Wh1KaYbdD8CllJj8 .error-icon{fill:#552222;}#mermaid-svg-Wh1KaYbdD8CllJj8 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Wh1KaYbdD8CllJj8 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Wh1KaYbdD8CllJj8 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Wh1KaYbdD8CllJj8 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Wh1KaYbdD8CllJj8 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Wh1KaYbdD8CllJj8 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Wh1KaYbdD8CllJj8 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Wh1KaYbdD8CllJj8 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Wh1KaYbdD8CllJj8 .marker.cross{stroke:#333333;}#mermaid-svg-Wh1KaYbdD8CllJj8 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Wh1KaYbdD8CllJj8 p{margin:0;}#mermaid-svg-Wh1KaYbdD8CllJj8 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Wh1KaYbdD8CllJj8 .cluster-label text{fill:#333;}#mermaid-svg-Wh1KaYbdD8CllJj8 .cluster-label span{color:#333;}#mermaid-svg-Wh1KaYbdD8CllJj8 .cluster-label span p{background-color:transparent;}#mermaid-svg-Wh1KaYbdD8CllJj8 .label text,#mermaid-svg-Wh1KaYbdD8CllJj8 span{fill:#333;color:#333;}#mermaid-svg-Wh1KaYbdD8CllJj8 .node rect,#mermaid-svg-Wh1KaYbdD8CllJj8 .node circle,#mermaid-svg-Wh1KaYbdD8CllJj8 .node ellipse,#mermaid-svg-Wh1KaYbdD8CllJj8 .node polygon,#mermaid-svg-Wh1KaYbdD8CllJj8 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Wh1KaYbdD8CllJj8 .rough-node .label text,#mermaid-svg-Wh1KaYbdD8CllJj8 .node .label text,#mermaid-svg-Wh1KaYbdD8CllJj8 .image-shape .label,#mermaid-svg-Wh1KaYbdD8CllJj8 .icon-shape .label{text-anchor:middle;}#mermaid-svg-Wh1KaYbdD8CllJj8 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Wh1KaYbdD8CllJj8 .rough-node .label,#mermaid-svg-Wh1KaYbdD8CllJj8 .node .label,#mermaid-svg-Wh1KaYbdD8CllJj8 .image-shape .label,#mermaid-svg-Wh1KaYbdD8CllJj8 .icon-shape .label{text-align:center;}#mermaid-svg-Wh1KaYbdD8CllJj8 .node.clickable{cursor:pointer;}#mermaid-svg-Wh1KaYbdD8CllJj8 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Wh1KaYbdD8CllJj8 .arrowheadPath{fill:#333333;}#mermaid-svg-Wh1KaYbdD8CllJj8 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Wh1KaYbdD8CllJj8 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Wh1KaYbdD8CllJj8 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Wh1KaYbdD8CllJj8 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Wh1KaYbdD8CllJj8 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Wh1KaYbdD8CllJj8 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Wh1KaYbdD8CllJj8 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Wh1KaYbdD8CllJj8 .cluster text{fill:#333;}#mermaid-svg-Wh1KaYbdD8CllJj8 .cluster span{color:#333;}#mermaid-svg-Wh1KaYbdD8CllJj8 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Wh1KaYbdD8CllJj8 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Wh1KaYbdD8CllJj8 rect.text{fill:none;stroke-width:0;}#mermaid-svg-Wh1KaYbdD8CllJj8 .icon-shape,#mermaid-svg-Wh1KaYbdD8CllJj8 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Wh1KaYbdD8CllJj8 .icon-shape p,#mermaid-svg-Wh1KaYbdD8CllJj8 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Wh1KaYbdD8CllJj8 .icon-shape .label rect,#mermaid-svg-Wh1KaYbdD8CllJj8 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Wh1KaYbdD8CllJj8 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Wh1KaYbdD8CllJj8 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Wh1KaYbdD8CllJj8 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 通过
失败




进入 add_audios
解析 audio_infos JSON
校验必填字段与范围
下载音频文件
抛出参数错误
检测音频实际时长
计算片段时间范围(start/end/duration)
创建 AudioSegment(volume/时长)
是否指定效果?
查找并应用音频效果
创建音频轨道
添加片段到轨道
是否重叠?
微调开始时间并重试
保存草稿并返回
结束

/v1/get_audio_duration 接口

  • 接口定位
    • 获取音频文件时长,返回微秒级时长
  • 请求参数
    • mp3_url:音频文件URL(支持常见音频格式)
  • 响应参数
    • duration:音频时长(微秒,>=0)
  • 实现要点
    • 服务层先下载到临时目录,再调用 get_media_duration(...) 通过 ffprobe 获取时长,最后清理临时文件
    • 若检测失败,抛出 AUDIO_DURATION_GET_FAILED 错误

"工具(download/get_media_duration)" "服务层(get_audio_duration)" "模型层(Pydantic)" "路由层(v1)" "客户端" "工具(download/get_media_duration)" "服务层(get_audio_duration)" "模型层(Pydantic)" "路由层(v1)" "客户端" #mermaid-svg-I2CYg9lNbZI5zIqy{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-I2CYg9lNbZI5zIqy .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-I2CYg9lNbZI5zIqy .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-I2CYg9lNbZI5zIqy .error-icon{fill:#552222;}#mermaid-svg-I2CYg9lNbZI5zIqy .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-I2CYg9lNbZI5zIqy .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-I2CYg9lNbZI5zIqy .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-I2CYg9lNbZI5zIqy .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-I2CYg9lNbZI5zIqy .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-I2CYg9lNbZI5zIqy .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-I2CYg9lNbZI5zIqy .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-I2CYg9lNbZI5zIqy .marker{fill:#333333;stroke:#333333;}#mermaid-svg-I2CYg9lNbZI5zIqy .marker.cross{stroke:#333333;}#mermaid-svg-I2CYg9lNbZI5zIqy svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-I2CYg9lNbZI5zIqy p{margin:0;}#mermaid-svg-I2CYg9lNbZI5zIqy .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-I2CYg9lNbZI5zIqy text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-I2CYg9lNbZI5zIqy .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-I2CYg9lNbZI5zIqy .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-I2CYg9lNbZI5zIqy .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-I2CYg9lNbZI5zIqy .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-I2CYg9lNbZI5zIqy #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-I2CYg9lNbZI5zIqy .sequenceNumber{fill:white;}#mermaid-svg-I2CYg9lNbZI5zIqy #sequencenumber{fill:#333;}#mermaid-svg-I2CYg9lNbZI5zIqy #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-I2CYg9lNbZI5zIqy .messageText{fill:#333;stroke:none;}#mermaid-svg-I2CYg9lNbZI5zIqy .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-I2CYg9lNbZI5zIqy .labelText,#mermaid-svg-I2CYg9lNbZI5zIqy .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-I2CYg9lNbZI5zIqy .loopText,#mermaid-svg-I2CYg9lNbZI5zIqy .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-I2CYg9lNbZI5zIqy .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-I2CYg9lNbZI5zIqy .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-I2CYg9lNbZI5zIqy .noteText,#mermaid-svg-I2CYg9lNbZI5zIqy .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-I2CYg9lNbZI5zIqy .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-I2CYg9lNbZI5zIqy .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-I2CYg9lNbZI5zIqy .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-I2CYg9lNbZI5zIqy .actorPopupMenu{position:absolute;}#mermaid-svg-I2CYg9lNbZI5zIqy .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-I2CYg9lNbZI5zIqy .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-I2CYg9lNbZI5zIqy .actor-man circle,#mermaid-svg-I2CYg9lNbZI5zIqy line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-I2CYg9lNbZI5zIqy :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} POST /v1/get_audio_duration 校验请求参数 调用 get_audio_duration(mp3_url) download(mp3_url, TEMP_DIR) 本地临时文件路径 get_media_duration(path) 时长(微秒) 返回 duration 200 OK 响应

音频信息数据结构与配置

  • audio_infos 数组元素字段
    • audio_url:音频文件URL(必填)
    • start:开始时间(微秒,必填)
    • end:结束时间(微秒,必填)
    • duration:音频总时长(微秒,可选;未提供时将自动检测)
    • volume:音量(0.0-2.0,可选,默认1.0)
    • audio_effect:音频效果名称(可选)
  • 时间参数说明
    • start/end 以微秒为单位;实际播放时长 = end - start
  • 音量控制
    • 0.0 表示静音,1.0 表示原始音量,0.5 表示减半音量;范围建议为 0.0-2.0
  • 音频效果
    • 支持通过名称匹配效果类型,参数会被转换到 0-100 范围后应用

音频轨道与时间线布局

  • 轨道创建
    • 服务层创建新的音频轨道,名称包含唯一标识,相对渲染索引设置为10,避免与主轨道冲突
  • 片段添加与重叠处理
    • 片段添加时若发生重叠,服务层会逐步增加开始时间偏移(每次+100微秒),最多尝试若干次,直至不重叠或达到最大尝试次数
  • 结束时间与最小持续时间
    • 片段持续时间不得为零或负数,若计算为非正值,将设置最小持续时间为100微秒,避免重叠问题

音频效果应用

  • 效果查找与参数转换
    • 服务层根据效果名称在多种效果类型中查找匹配项,若找到则将默认参数转换为 0-100 范围后应用
  • 效果对象
    • AudioEffect 与 AudioSegment 配合,支持场景音、音色、声音成曲等分类

音频格式支持、采样率与文件大小

  • 格式支持
    • 通过 ffprobe 检测时长,理论上支持 ffprobe 能识别的常见音频格式(如 MP3、WAV、M4A 等)
  • 采样率与质量
    • 代码未对采样率进行显式限制;最终效果取决于底层剪映对素材的处理
  • 文件大小
    • 服务层会下载到临时目录,大文件可能影响下载与处理性能;建议优先使用较小或压缩后的音频文件

最佳实践

  • 音频同步
    • 使用统一时间基准(微秒),确保各音频片段的 start/end 与整体时长一致
  • 多轨混合
    • 将不同类型的音频(背景音乐、音效、旁白)分别放置在独立音频轨道,便于单独调节音量与应用效果
  • 音频效果处理
    • 优先使用预设效果名称,避免复杂参数配置;如需精细控制,可在支持的前提下通过关键帧实现动态音量变化
  • 性能优化
    • 预先获取音频时长,减少重复检测;合理规划片段时长,避免过长片段导致重叠与反复调整

依赖关系分析

  • 组件耦合
    • 路由层仅负责参数绑定与响应封装,业务逻辑集中在服务层
    • 服务层依赖工具层(下载与媒体检测)与底层剪映模型(轨道与片段)
  • 外部依赖
    • ffprobe:用于媒体时长检测
    • 剪映草稿模型:用于轨道与片段的创建与管理
  • 错误边界
    • 所有异常通过自定义异常类与错误码统一返回,便于前端与客户端处理

#mermaid-svg-W9tTEktSdBERG0xL{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-W9tTEktSdBERG0xL .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-W9tTEktSdBERG0xL .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-W9tTEktSdBERG0xL .error-icon{fill:#552222;}#mermaid-svg-W9tTEktSdBERG0xL .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-W9tTEktSdBERG0xL .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-W9tTEktSdBERG0xL .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-W9tTEktSdBERG0xL .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-W9tTEktSdBERG0xL .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-W9tTEktSdBERG0xL .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-W9tTEktSdBERG0xL .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-W9tTEktSdBERG0xL .marker{fill:#333333;stroke:#333333;}#mermaid-svg-W9tTEktSdBERG0xL .marker.cross{stroke:#333333;}#mermaid-svg-W9tTEktSdBERG0xL svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-W9tTEktSdBERG0xL p{margin:0;}#mermaid-svg-W9tTEktSdBERG0xL .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-W9tTEktSdBERG0xL .cluster-label text{fill:#333;}#mermaid-svg-W9tTEktSdBERG0xL .cluster-label span{color:#333;}#mermaid-svg-W9tTEktSdBERG0xL .cluster-label span p{background-color:transparent;}#mermaid-svg-W9tTEktSdBERG0xL .label text,#mermaid-svg-W9tTEktSdBERG0xL span{fill:#333;color:#333;}#mermaid-svg-W9tTEktSdBERG0xL .node rect,#mermaid-svg-W9tTEktSdBERG0xL .node circle,#mermaid-svg-W9tTEktSdBERG0xL .node ellipse,#mermaid-svg-W9tTEktSdBERG0xL .node polygon,#mermaid-svg-W9tTEktSdBERG0xL .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-W9tTEktSdBERG0xL .rough-node .label text,#mermaid-svg-W9tTEktSdBERG0xL .node .label text,#mermaid-svg-W9tTEktSdBERG0xL .image-shape .label,#mermaid-svg-W9tTEktSdBERG0xL .icon-shape .label{text-anchor:middle;}#mermaid-svg-W9tTEktSdBERG0xL .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-W9tTEktSdBERG0xL .rough-node .label,#mermaid-svg-W9tTEktSdBERG0xL .node .label,#mermaid-svg-W9tTEktSdBERG0xL .image-shape .label,#mermaid-svg-W9tTEktSdBERG0xL .icon-shape .label{text-align:center;}#mermaid-svg-W9tTEktSdBERG0xL .node.clickable{cursor:pointer;}#mermaid-svg-W9tTEktSdBERG0xL .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-W9tTEktSdBERG0xL .arrowheadPath{fill:#333333;}#mermaid-svg-W9tTEktSdBERG0xL .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-W9tTEktSdBERG0xL .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-W9tTEktSdBERG0xL .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-W9tTEktSdBERG0xL .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-W9tTEktSdBERG0xL .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-W9tTEktSdBERG0xL .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-W9tTEktSdBERG0xL .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-W9tTEktSdBERG0xL .cluster text{fill:#333;}#mermaid-svg-W9tTEktSdBERG0xL .cluster span{color:#333;}#mermaid-svg-W9tTEktSdBERG0xL div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-W9tTEktSdBERG0xL .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-W9tTEktSdBERG0xL rect.text{fill:none;stroke-width:0;}#mermaid-svg-W9tTEktSdBERG0xL .icon-shape,#mermaid-svg-W9tTEktSdBERG0xL .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-W9tTEktSdBERG0xL .icon-shape p,#mermaid-svg-W9tTEktSdBERG0xL .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-W9tTEktSdBERG0xL .icon-shape .label rect,#mermaid-svg-W9tTEktSdBERG0xL .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-W9tTEktSdBERG0xL .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-W9tTEktSdBERG0xL .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-W9tTEktSdBERG0xL :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 路由层
服务层
工具层(download/get_media_duration)
剪映草稿模型(AudioSegment/Track)
配置(config.py)
错误码(exceptions.py)

性能考量

  • 下载与检测
    • 大音频文件会增加下载与 ffprobe 检测耗时;建议在调用前预先获取时长并缓存
  • 片段重叠调整
    • 当发生重叠时,服务层会多次尝试微调开始时间;尽量避免密集片段叠加,减少重试次数
  • 资源清理
    • 服务层在获取时长后会清理临时文件,避免磁盘占用;请确保临时目录空间充足

故障排查指南

  • 常见错误与解决
    • 草稿URL无效:确认 draft_url 中 draft_id 存在于缓存中
    • audio_infos 缺失或格式错误:确保为合法JSON字符串数组,且每项包含 audio_url、start、end
    • 音量越界:将音量限制在 0.0-2.0
    • 时长非法:duration 必须为正数;未提供时将自动检测
    • 片段重叠:检查 start/end 是否与其他片段冲突;必要时微调时间
    • 音频资源不可访问:确认 audio_url 可正常访问
  • 错误码参考
    • 2007:无效的音频信息
    • 2008:音频添加失败
    • 2034:获取音频时长失败
    • 1002:资源不存在
    • 9998:系统内部错误

结论

/v1/add_audios 与 /v1/get_audio_duration 为音频处理的核心能力,前者负责批量添加与轨道管理,后者负责时长检测。通过明确的参数规范、完善的错误处理与合理的重叠处理策略,可稳定地实现多轨音频混合与效果应用。

建议结合最佳实践,提前规划时间线与效果,以获得更优的处理性能与产出质量。

附录

接口清单与使用要点

  • /v1/add_audios
    • 请求:draft_url、audio_infos(JSON字符串数组)
    • 响应:draft_url、track_id、audio_ids
    • 适用场景:批量添加背景音乐、音效、旁白等
  • /v1/get_audio_duration
    • 请求:mp3_url
    • 响应:duration(微秒)
    • 适用场景:预估时长、统一时间基准

测试参考

  • 手动测试脚本
    • /v1/add_audios:创建草稿后调用添加音频,断言返回的 track_id 与 audio_ids
    • /v1/get_audio_duration:发送请求并验证响应包含 duration 字段

文档信息

相关推荐
KaMeidebaby1 小时前
卡梅德生物技术快报|蛋白翻译后修饰:YAP/TAZ 分子调控机制与靶向干预技术
前端·人工智能·物联网·百度·新浪微博
JustHappy1 小时前
古法编程秘籍(三):为什么需要函数?因为程序员讨厌重复劳动
前端·javascript·后端
weixin_397574091 小时前
AgentRAG与ReAct推理链:从检索增强到推理增强
前端·react.js·前端框架
若兰幽竹1 小时前
【HarmonyOS 6.1 全场景实战】《灵犀厨房》实战(二十二) | 多媒体 | AVPlayer嵌入教学视频——让智慧屏真正“活”起来
音视频·华为鸿蒙系统·harmonyos6.1.0·灵犀厨房·harmonyos6.1
chenying9981791 小时前
扩散模型语音克隆:参考音频注入的五种方式
人工智能·音视频·语音合成
想要狠赚笔的小燕2 小时前
vue项目的入口文件是什么 main.js还是index.html,他俩有啥区别
前端·javascript
Jiude2 小时前
AI面对真机调试也束手无策?我将方法论形成了一套SKILL 🛠️🤖
前端·后端·测试
之歆2 小时前
Day02_ES6+ 核心特性深度解析:现代 JavaScript 开发的基石
前端·javascript·es6
问心无愧05132 小时前
ctf show web入门71
android·前端·笔记