AI 生视频 v3.0
描述
根据文本描述生成视频,或结合输入图片和文本描述生成视频。支持文生视频和图生视频两种调用方式。
版本
1.0
输入要求
文生视频无需传入图片;图生视频通过 init_images 传入图片 URL。
调用 URL
正式环境:https://openapi.meitu.com
任务提交接口:
https://openapi.meitu.com/api/v1/sdk/sync/push任务名称(task):/v1/v3_mvv_model/ai_video/453181
任务类型(task_type):formula
调用方法
POST
Content-Type: application/json
权限
使用已开通该能力的 AK/SK,通过签名 SDK 生成请求签名。详见开放平台接口签名。
请求参数
| 是否必选 | 参数名 | 类型 | 参数说明 |
|---|---|---|---|
| 是 | task | string | 固定为 /v1/v3_mvv_model/ai_video/453181。 |
| 是 | task_type | string | 固定为 formula。 |
| 否 | init_images | object[] | 输入图片列表。文生视频时省略;图生视频时传入。 |
| 是 | params | string | 算法参数对象序列化后的 JSON 字符串。 |
| 否 | sync_timeout | integer | 同步等待时长,单位为秒,网关默认值为 30。 |
init_images 元素
| 是否必选 | 参数名 | 类型 | 参数说明 |
|---|---|---|---|
| 是 | url | string | 可访问的输入图片 URL。 |
| 是 | profile | object | 图片配置。 |
profile
| 是否必选 | 参数名 | 类型 | 参数说明 |
|---|---|---|---|
| 是 | version | string | 固定为 v1。 |
| 是 | media_profiles | object | 图片传输属性,本接口示例传入空对象 {}。 |
params
解码后的对象直接包含以下字段,不添加 parameter 包装层:
| 是否必选 | 参数名 | 类型 | 参数说明 |
|---|---|---|---|
| 是 | user_prompt | string | 视频生成的文本描述。 |
输入值示例
替换示例中的文本描述及图片 URL。HTTP 请求体为 JSON 对象,仅 params 的值为 JSON 字符串。
文生视频
{
"task": "/v1/v3_mvv_model/ai_video/453181",
"task_type": "formula",
"params": "{\"user_prompt\":\"A gentle breeze moves through the trees.\"}",
"sync_timeout": 30
}图生视频
{
"task": "/v1/v3_mvv_model/ai_video/453181",
"task_type": "formula",
"init_images": [
{
"url": "https://example.com/input.jpg",
"profile": {
"version": "v1",
"media_profiles": {}
}
}
],
"params": "{\"user_prompt\":\"A gentle breeze moves through the trees.\"}",
"sync_timeout": 30
}params 解码后的对象:
{
"user_prompt": "A gentle breeze moves through the trees."
}返回值说明
code = 0 表示请求处理正常,仍需检查 data.status。当状态为 10 时,从 data.result.urls 读取结果地址。请及时下载保存结果。
| 字段 | 类型 | 参数说明 |
|---|---|---|
| code | integer | 业务状态码,0 表示请求处理正常。 |
| message | string | 响应消息,失败时可用于排查错误。 |
| data | object / null | 任务详情;请求失败时可能为 null。 |
data
| 字段 | 类型 | 参数说明 |
|---|---|---|
| status | integer | -1 未找到任务;0 创建成功;1 执行中;2 失败;9 同步等待超时,需要继续查询;10 成功。 |
| msg | string | 任务消息。 |
| result | object | 任务结果或任务标识。 |
| progress | number | 任务进度,例如 0.1、0.85、1。 |
result
| 字段 | 类型 | 参数说明 |
|---|---|---|
| id | string | 网关任务 ID,用于查询任务结果。 |
| urls | string[] | 成功时返回的结果地址列表。 |
返回值示例
以下成功和失败结构来自原接口文档,任务 ID、结果地址已替换为占位值,未进行实际调用。原文结果地址以 .jpg 结尾,未明确视频封装格式或固定输出数量;以下地址不表示输出格式及数量保证。
请求成功返回示例
HTTP 200,Content-Type: application/json。
{
"code": 0,
"data": {
"status": 10,
"result": {
"id": "example-task-id",
"urls": [
"https://example.com/result-1",
"https://example.com/result-2"
]
},
"progress": 1
}
}需要查询返回示例
以下为 status = 9 的结构示意。同步等待超时不代表任务失败,使用 data.result.id 按任务查询接口继续查询,不要重复提交。
{
"code": 0,
"data": {
"status": 9,
"result": {
"id": "example-task-id"
},
"progress": 0
}
}请求失败返回示例
原文失败示例为 HTTP 400。其他错误的状态码和消息以实际响应为准。
{
"code": 20008,
"message": "UNSUITABLE_IMAGE",
"data": null
}通用的错误代码与信息
| 错误码 | 错误信息 | 说明 |
|---|---|---|
| 20001 | PROCESS_ERROR | 处理错误。 |
| 20008 | UNSUITABLE_IMAGE | 图片不符合处理要求,见上方失败示例。 |
其他错误详见 API 错误码。
SDK 调用示例
以下示例均使用上方图生视频请求,内嵌完整请求体,无需额外 JSON 文件。按对应文档引入签名 SDK 后,替换 AK、SK、图片 URL 和文本描述。
文生视频时省略 init_images。示例提交一次并打印响应;签名和发送必须使用相同的最终请求体,返回状态为 9 时继续查询任务。
Python
import json
import requests
from sign_sdk import sign
def main():
key = "YOUR_ACCESS_KEY"
secret = "YOUR_SECRET_KEY"
url = "https://openapi.meitu.com/api/v1/sdk/sync/push"
inner_params = {
"user_prompt": "A gentle breeze moves through the trees."
}
payload = {
"task": "/v1/v3_mvv_model/ai_video/453181",
"task_type": "formula",
"init_images": [
{
"url": "https://example.com/input.jpg",
"profile": {
"version": "v1",
"media_profiles": {}
}
}
],
"params": json.dumps(inner_params, ensure_ascii=True),
"sync_timeout": 30
}
body = json.dumps(payload, ensure_ascii=True)
headers = {
"Content-Type": "application/json",
sign.HeaderHost: "openapi.meitu.com",
}
signer = sign.Signer(key, secret)
signed_request = signer.sign(url, "POST", headers, body)
with requests.Session() as session:
response = session.send(
signed_request, timeout=(10, 60), verify=True, allow_redirects=False
)
print("Status:", response.status_code)
print("Response:", response.text)
if __name__ == "__main__":
main()
Go
package main
import (
"fmt"
"io"
"net/http"
"time"
"github.com/formula/api/signer"
)
func main() {
body := `{
"task": "/v1/v3_mvv_model/ai_video/453181",
"task_type": "formula",
"init_images": [
{
"url": "https://example.com/input.jpg",
"profile": {
"version": "v1",
"media_profiles": {}
}
}
],
"params": "{\"user_prompt\":\"A gentle breeze moves through the trees.\"}",
"sync_timeout": 30
}`
url := "https://openapi.meitu.com/api/v1/sdk/sync/push"
headers := make(http.Header)
headers.Set("Content-Type", "application/json")
headers.Set(signer.HeaderHost, "openapi.meitu.com")
signObj := signer.NewSigner("YOUR_ACCESS_KEY", "YOUR_SECRET_KEY")
req, err := signObj.Sign(url, http.MethodPost, headers, body)
if err != nil {
fmt.Println("Sign request failed:", err)
return
}
client := &http.Client{
Timeout: 60 * time.Second,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
resp, err := client.Do(req)
if err != nil {
fmt.Println("Send request failed:", err)
return
}
defer resp.Body.Close()
responseBody, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("Read response failed:", err)
return
}
fmt.Println("Status:", resp.StatusCode)
fmt.Println("Response:", string(responseBody))
}
PHP
<?php
require_once __DIR__ . '/signer.php';
$innerParams = json_encode([
'user_prompt' => 'A gentle breeze moves through the trees.'
], JSON_UNESCAPED_SLASHES);
$body = json_encode([
'task' => '/v1/v3_mvv_model/ai_video/453181',
'task_type' => 'formula',
'init_images' => [
[
'url' => 'https://example.com/input.jpg',
'profile' => [
'version' => 'v1',
'media_profiles' => (object) []
]
]
],
'params' => $innerParams,
'sync_timeout' => 30
], JSON_UNESCAPED_SLASHES);
$url = 'https://openapi.meitu.com/api/v1/sdk/sync/push';
$headers = [
'Content-Type' => 'application/json',
'Host' => 'openapi.meitu.com',
];
$signer = new Signer('YOUR_ACCESS_KEY', 'YOUR_SECRET_KEY');
$curl = $signer->sign($url, 'POST', $headers, $body);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($curl, CURLOPT_TIMEOUT, 60);
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);
$response = curl_exec($curl);
if ($response === false) {
echo 'Error: ' . curl_error($curl) . PHP_EOL;
} else {
echo 'Status: ' . curl_getinfo($curl, CURLINFO_HTTP_CODE) . PHP_EOL;
echo 'Response: ' . $response . PHP_EOL;
}
curl_close($curl);
Java
import com.meitu.openai.common.Signer;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) throws Exception {
String body =
"{\n" +
" \"task\": \"/v1/v3_mvv_model/ai_video/453181\",\n" +
" \"task_type\": \"formula\",\n" +
" \"init_images\": [\n" +
" {\n" +
" \"url\": \"https://example.com/input.jpg\",\n" +
" \"profile\": {\n" +
" \"version\": \"v1\",\n" +
" \"media_profiles\": {}\n" +
" }\n" +
" }\n" +
" ],\n" +
" \"params\": \"{\\\"user_prompt\\\":\\\"A gentle breeze moves through the trees.\\\"}\",\n" +
" \"sync_timeout\": 30\n" +
"}";
String url = "https://openapi.meitu.com/api/v1/sdk/sync/push";
Map<String, String> headers = new HashMap<>();
headers.put("Content-Type", "application/json");
headers.put(Signer.HeaderHost, "openapi.meitu.com");
Signer signer = new Signer("YOUR_ACCESS_KEY", "YOUR_SECRET_KEY");
Map<String, String> signedHeaders = signer.sign(url, "POST", headers, body);
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
try {
connection.setRequestMethod("POST");
connection.setConnectTimeout(10000);
connection.setReadTimeout(60000);
connection.setInstanceFollowRedirects(false);
connection.setDoOutput(true);
for (Map.Entry<String, String> entry : signedHeaders.entrySet()) {
connection.setRequestProperty(entry.getKey(), entry.getValue());
}
byte[] bodyBytes = body.getBytes(StandardCharsets.UTF_8);
connection.setFixedLengthStreamingMode(bodyBytes.length);
try (OutputStream output = connection.getOutputStream()) {
output.write(bodyBytes);
}
int status = connection.getResponseCode();
System.out.println("Status: " + status);
InputStream responseStream = status >= 400
? connection.getErrorStream() : connection.getInputStream();
if (responseStream != null) {
try (InputStream input = responseStream;
ByteArrayOutputStream output = new ByteArrayOutputStream()) {
byte[] buffer = new byte[4096];
int length;
while ((length = input.read(buffer)) != -1) {
output.write(buffer, 0, length);
}
System.out.println("Response: " + output.toString("UTF-8"));
}
}
} finally {
connection.disconnect();
}
}
}