字幕水印一键全消
描述
提交视频进行一键清屏处理。算法异步执行,可通过任务查询接口获取处理结果。
版本
1.0
视频要求
仅支持通过 URL 传入视频。原文未明确视频编码、文件大小、分辨率或时长限制。
调用 URL
任务提交接口:
https://openapi.meitu.com/api/v1/sdk/sync/push任务名称(task):/v1/Video_Remove/496462
任务类型(task_type):formula
调用方法
POST
Content-Type: application/json
权限
使用已开通该能力的 AK/SK,通过签名 SDK 生成请求签名。详见开放平台接口签名。
请求参数
| 是否必选 | 参数名 | 类型 | 参数说明 |
|---|---|---|---|
| 是 | task | string | 固定为 /v1/Video_Remove/496462。 |
| 是 | task_type | string | 固定为 formula。 |
| 是 | init_images | object[] | 输入视频列表,字段名保留为 init_images。 |
| 是 | params | string | 算法参数对象序列化后的 JSON 字符串。 |
| 否 | sync_timeout | integer | 同步等待时长,单位为秒,默认值为 30。 |
init_images 元素
| 是否必选 | 参数名 | 类型 | 参数说明 |
|---|---|---|---|
| 是 | url | string | 输入视频 URL,不进行 Base64 编码。 |
| 是 | profile | object | 视频属性信息。 |
profile
| 是否必选 | 参数名 | 类型 | 参数说明 |
|---|---|---|---|
| 是 | version | string | 网关媒体配置版本,固定为 v1。 |
| 是 | media_profiles | object | 视频传输和处理属性。 |
media_profiles
| 是否必选 | 参数名 | 类型 | 参数说明 |
|---|---|---|---|
| 是 | media_data_type | string | 本文按原文请求示例使用 url,表示输入为视频 URL。 |
| 否 | skip_frames | array | [a,b]:开头跳过 a 帧,结尾跳过 b 帧,用于切片。默认值为 [0,0]。 |
| 否 | encoder_rate_scale | number | 视频编码码率膨胀系数。原文未说明默认值或取值范围。 |
params
params 必须为 JSON 字符串。解码后包含以下字段:
| 是否必选 | 参数名 | 类型 | 参数说明 |
|---|---|---|---|
| 是 | parameter | object | 一键清屏算法参数。 |
| 否 | extra | object | 算法协议中的额外处理信息,无需传入时可省略。 |
parameter
| 是否必选 | 参数名 | 类型 | 参数说明 |
|---|---|---|---|
| 否 | userboxes | string | 用户交互区域框的坐标字符串。每个框使用四个相对坐标点,顺序为左上、右上、右下、左下;默认无。 |
| 否 | protectboxes | string | 保护区域框的坐标字符串。每个框的坐标顺序同 userboxes;默认无。 |
单个框的字符串值示例:
[[0.1,0.1],[0.2,0.1],[0.2,0.2],[0.1,0.2]]需要传入区域框时,params 解码后的对象示例如下。注意 userboxes 和 protectboxes 仍为字符串,不是数组:
{
"parameter": {
"userboxes": "[[0.1,0.1],[0.2,0.1],[0.2,0.2],[0.1,0.2]]",
"protectboxes": "[[0.6,0.6],[0.8,0.6],[0.8,0.8],[0.6,0.8]]"
}
}原文说明可传多个框,但未明确多个框之间的分组结构,本文不扩展多框示例。原文参数表中的输入媒体类型为 MP4,与其请求示例的 url 不一致;本文输入采用原文示例的 URL 方式。
输入值示例
以下示例通过 userboxes 指定一个交互区域框,不设置保护框。请按需替换视频 URL 和区域坐标。
{
"task": "/v1/Video_Remove/496462",
"task_type": "formula",
"init_images": [
{
"url": "https://example.com/input.mp4",
"profile": {
"media_profiles": {
"media_data_type": "url",
"skip_frames": [
0,
0
]
},
"version": "v1"
}
}
],
"params": "{\"parameter\":{\"userboxes\":\"[[0.1,0.1],[0.2,0.1],[0.2,0.2],[0.1,0.2]]\"}}",
"sync_timeout": 30
}返回值说明
以下采用统一网关的任务封装说明;本 task 的实际结果封装层级尚未通过响应样本核验。原文明确的算法结果字段单独列于下文。
| 字段 | 类型 | 说明 |
|---|---|---|
| code | integer | 网关业务状态码。0 表示请求处理正常,不代表任务已经完成。 |
| message | string | 网关响应信息。 |
| data | object | 任务状态和结果。 |
| data.task_id | string | 网关任务 ID,用于任务查询。 |
| data.status | integer | 任务状态,见下表。 |
| data.result | object | 结果封装;下文结构示例将算法结果放在 data.result.data。 |
| data.status | 说明 |
|---|---|
| -1 | 未找到任务。 |
| 0 | 任务创建成功。 |
| 1 | 执行中。 |
| 2 | 失败。 |
| 9 | 同步等待超时,继续查询,不代表算法任务失败。 |
| 10 | 成功。 |
result
| 字段 | 类型 | 说明 |
|---|---|---|
| id | string | 网关任务标识。 |
| code | integer | 算法状态码。 |
| msg | string | 算法响应消息。 |
| msg_id | string | 算法请求标识,不用于替代网关 task_id。 |
| data | object | 算法原始响应。 |
算法结果字段
| 字段 | 类型 | 说明 |
|---|---|---|
| msg_id | string | 算法请求标识。原文说明该标识及算法结果保留 24 小时,之后删除。 |
| media_info_list | object[] | 处理结果列表。 |
| media_info_list[].media_data | string | 结果媒体数据;本文 URL 结构示例中为视频地址。 |
| media_info_list[].media_profiles | object | 结果媒体属性。 |
| parameter | object | 输出类型、算法版本及消除状态。 |
| parameter.rsp_media_type | string | 输出方式;本文结构示例使用 url。 |
| parameter.version | string | 算法版本号。 |
| parameter.exist_text | boolean | 是否对视频进行了消除。未检测到待消除内容时,不执行消除。 |
| error_code | integer | 原文失败响应中的算法错误码。 |
| error_msg | string | 原文失败响应中的算法错误信息。 |
原文返回字段说明仍包含 jpg / Base64 表述,没有提供完整的视频成功响应 JSON;本文不据此声明支持 Base64 视频输出。
网关查询使用 data.task_id,不是算法 msg_id。请及时查询并保存结果;原文的 24 小时说明针对算法标识及结果,不是本 task 的网关留存承诺。
返回值示例
以下为根据统一网关模板和原文算法字段整理的结构示例,非实际调用记录。响应层级、具体值及错误信息需以该 task 的实际响应为准。
成功结构示例
{
"code": 0,
"message": "success",
"data": {
"status": 10,
"task_id": "example-task-id",
"result": {
"id": "example-task-id",
"code": 0,
"msg": "success",
"msg_id": "example-message-id",
"data": {
"msg_id": "example-message-id",
"media_info_list": [
{
"media_data": "https://example.com/result.mp4",
"media_profiles": {
"media_data_type": "url"
}
}
],
"parameter": {
"rsp_media_type": "url",
"version": "1.0",
"exist_text": true
}
}
}
}
}需要查询结构示例
当 data.status = 9 时,使用 data.task_id 调用任务查询接口,不要因同步等待超时重复提交任务。
{
"code": 0,
"message": "success",
"data": {
"status": 9,
"task_id": "example-task-id",
"result": {
"id": "example-task-id"
}
}
}失败结构示例
以下以 20001 / ALGO_MODEL_CRASH 说明失败结构,不表示所有失败都返回该错误。
{
"code": 20001,
"message": "ALGO_MODEL_CRASH",
"data": {
"status": 2,
"task_id": "example-task-id",
"result": {
"id": "example-task-id",
"code": 20001,
"msg": "ALGO_MODEL_CRASH",
"msg_id": "example-message-id",
"data": {
"error_code": 20001,
"error_msg": "ALGO_MODEL_CRASH",
"msg_id": "example-message-id"
}
}
}
}通用的错误代码与信息
详见 API 错误码。
SDK 调用示例
以下示例内嵌完整请求体,无需额外 JSON 文件。按对应文档引入签名 SDK 后,替换 AK、SK 和视频 URL。
示例提交一次并打印响应;任务未完成时,按任务查询文档获取结果。
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"
params = {
"parameter": {
"userboxes": "[[0.1,0.1],[0.2,0.1],[0.2,0.2],[0.1,0.2]]"
}
}
payload = {
"task": "/v1/Video_Remove/496462",
"task_type": "formula",
"init_images": [
{
"url": "https://example.com/input.mp4",
"profile": {
"media_profiles": {
"media_data_type": "url",
"skip_frames": [
0,
0
]
},
"version": "v1"
}
}
],
"params": json.dumps(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/mtlab/api/signer"
)
func main() {
body := `{
"task": "/v1/Video_Remove/496462",
"task_type": "formula",
"init_images": [
{
"url": "https://example.com/input.mp4",
"profile": {
"media_profiles": {
"media_data_type": "url",
"skip_frames": [
0,
0
]
},
"version": "v1"
}
}
],
"params": "{\"parameter\":{\"userboxes\":\"[[0.1,0.1],[0.2,0.1],[0.2,0.2],[0.1,0.2]]\"}}",
"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';
$params = json_encode([
'parameter' => [
'userboxes' => '[[0.1,0.1],[0.2,0.1],[0.2,0.2],[0.1,0.2]]',
],
]);
$body = json_encode([
'task' => '/v1/Video_Remove/496462',
'task_type' => 'formula',
'init_images' => [
[
'url' => 'https://example.com/input.mp4',
'profile' => [
'media_profiles' => [
'media_data_type' => 'url',
'skip_frames' => [0, 0],
],
'version' => 'v1',
],
],
],
'params' => $params,
'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/Video_Remove/496462\",\n" +
" \"task_type\": \"formula\",\n" +
" \"init_images\": [\n" +
" {\n" +
" \"url\": \"https://example.com/input.mp4\",\n" +
" \"profile\": {\n" +
" \"media_profiles\": {\n" +
" \"media_data_type\": \"url\",\n" +
" \"skip_frames\": [\n" +
" 0,\n" +
" 0\n" +
" ]\n" +
" },\n" +
" \"version\": \"v1\"\n" +
" }\n" +
" }\n" +
" ],\n" +
" \"params\": \"{\\\"parameter\\\":{\\\"userboxes\\\":\\\"[[0.1,0.1],[0.2,0.1],[0.2,0.2],[0.1,0.2]]\\\"}}\",\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();
}
}
}