Video Eye Contact Correction
Description
This API submits a lip-sync delivery task. After a successful submission, the algorithm starts executing asynchronously. You can proactively query the algorithm execution result through the query API.
Version
1.0
Media Requirements
Media file requirements in the source document: wav, m4a, mp4, mp3, aac, and other formats are supported. The source document does not specify dimensions, file size, or duration limits.
API URL
Production environment: https://openapi.meitu.com
Task submission endpoint: https://openapi.meitu.com/api/v1/sdk/sync/push
Task name (task): /v1/mtgazecorrect_v2_vid/481969
Task type (task_type): formulaMethod
POST
Content-Type: application/json
Authentication
Request Parameters
| Required | Parameter | Type | Description |
|---|---|---|---|
| Yes | params | string | Algorithm parameters (JSON string) |
| Yes | init_images | object[] | List of media files |
| Yes | task | string | Fixed value: /v1/mtgazecorrect_v2_vid/481969 |
| Yes | task_type | string | Fixed value: formula |
| No | sync_timeout | int | Default: 30. Synchronous timeout; if the request times out, status is 9 and the result must be retrieved through the query API |
Structure of each media file in init_images:
| Required | Parameter | Type | Description |
|---|---|---|---|
| Yes | url | string | Media URL or base64 data |
| Yes | profile | object | Media attribute information |
profile attribute information:
| Required | Parameter | Type | Description |
|---|---|---|---|
| Yes | media_profiles | object | Media properties |
| Yes | version | string | Fixed value: v1 |
media_profiles media properties:
| Required | Parameter | Type | Description |
|---|---|---|---|
| Yes | media_data_type | string | url indicates a URL; types such as jpg indicate base64 data |
The params inference parameter is a JSON string with the following structure:
| Required | Field | Type | Description |
|---|---|---|---|
| No | rsp_media_type | string | Default: url; jpg indicates base64 data |
| Yes | parameter | object | Core algorithm parameter object |
Details of the parameter algorithm parameters:
The original parameter table lists the eye contact correction fields directly under parameter, while the original request example places these fields under parameter.base. The nested structure from the original request example is shown below, while retaining the field descriptions from the original parameter table.
| Required | Parameter | Type | Description |
|---|---|---|---|
| Not specified in the source document | base | object | Eye contact correction parameter object; see the table below for its structure |
| Not specified in the source document | extra | object | Extended parameters; an empty object in the original request example |
| Not specified in the source document | version | string | Value in the original request example: 2.0.0 |
Details of the base algorithm parameters:
| Required | Parameter | Type | Description |
|---|---|---|---|
| No | close_thresh | float | Eye-closure threshold; default: 0.2 |
| No | vis_ratio | float | Visible landmark ratio; default: 0.49 |
| No | vis_thresh | float | Visible landmark confidence threshold; default: 0.91 |
| No | enhance_ratio | float | Enhancement factor; default: 1.0 |
| No | distance_error | float | Eyeball position determination threshold; default: 0.024 |
| Not specified in the source document | distance_error_y_plus | float | Value in the original request example: 0.9; meaning, default value, and required status are not specified in the source document |
| No | color_opt | bool | Local color correction; default: true |
| No | enable_tune | bool | Local fine-tuning; default: true |
| No | wink_opt | bool | Blink optimization; default: false |
| No | enable_enhance | bool | Detail enhancement; default: true |
Request Example
{
"task": "/v1/mtgazecorrect_v2_vid/481969",
"task_type": "formula",
"init_images": [
{
"url": "https://obs.mtlab.meitu.com/public/EtRGxLI8ulQ43pGhuZfym9aYUD2PUbMP/MTc0OTc4MDAwMA==/0281e99d-c48a-497f-5812-6ef391e200e3.mp4",
"profile": {
"media_profiles": {
"media_data_type": "url"
},
"version": "v1"
}
}
],
"params": "{\"rsp_media_type\":\"url\",\"parameter\":{\"base\":{\"close_thresh\":0.2,\"color_opt\":true,\"distance_error\":0.024,\"distance_error_y_plus\":0.9,\"enable_enhance\":true,\"enable_tune\":true,\"enhance_ratio\":1.0,\"vis_ratio\":0.49,\"vis_thresh\":0.91,\"wink_opt\":false},\"extra\":{},\"version\":\"2.0.0\"}}",
"sync_timeout": 30
}Response Fields
Generated results are periodically deleted. Download and save them promptly.| Field | Type | Description |
|---|---|---|
| request_id | string | Request identifier |
| trace_id | string | Trace identifier |
| code | int | Business status code; 0 indicates that the request was accepted successfully |
| error_code | int | Error code; 0 indicates success |
| message | string | Business message |
| tips | any | Additional information; may be null |
| data | object | Task status and algorithm result |
Fields in data:
| Field | Type | Description |
|---|---|---|
| status | int | Status code: -1, task not found; 0, created successfully; 1, processing; 2, failed; 9, timed out—use the query API; 10, succeeded |
| result | object | Algorithm result |
| progress | number | Task progress (0–100); 0 indicates not started or newly created |
| predict_elapsed | int | Estimated processing time in milliseconds |
| create_time | int64 | Creation timestamp in milliseconds |
| task_id | string | Task ID |
| custom_task_id | string | Client-defined task ID |
| trace_id | string | Trace identifier within data |
| client_info | string | Client information |
| init_images | object[]/null | Echoed input media |
Fields in result:
| Field | Type | Description |
|---|---|---|
| id | string | Task ID; the original field table specifies int, while the original response example uses string |
| urls | string[] | List of result media URLs |
| parameters | object | Task parameter information |
| data | object | Task result information; see the table below for its structure |
| predict_elapsed | int | Estimated processing time; the source document specifies seconds |
| msg | string | The original field table specifies long and describes it as the task creation timestamp in milliseconds, while the original response example uses a message string |
| msg_id | string | Unique string used to distinguish each request |
| images | string[] | List of result media URLs |
| media_info_list | object[] | List of media result information |
| code | int | Algorithm error code; 26301 in the failure example |
Fields in result.data:
| Field | Type | Description |
|---|---|---|
| media_info_list | object[] | Each item contains media information |
| parameter | object/null | Returned information type identifier |
| error_code | int | Returned error code; 0 indicates success |
| error_msg | string | Returned error message |
| msg_id | string | Unique request identifier; the source document states that this identifier and the algorithm result are retained for only 24 hours |
| duration | object | Detailed timing information |
| extra | object | Extended information |
Fields in media_info_list:
| Field | Type | Description |
|---|---|---|
| media_data | string | The original field table describes this as an input URL required for decoding by the VAE API (such as a .npy file URL); the original response example for this API uses an mp4 URL |
| media_extra | null | Reserved field; currently null |
| media_profiles | object | Additional media information; see the table below |
Fields in media_profiles:
| Field | Type | Description |
|---|---|---|
| media_data_fps | float | Video frame rate |
| media_data_height | int | Video height |
| media_data_size | int | Video data size in bytes |
| media_data_type | string | Type of the returned media_data; for example, url indicates an address |
| media_data_width | int | Video width |
Fields in duration:
| Field | Type | Description |
|---|---|---|
| created_timestamp | int | Timestamp when the algorithm receives the request, in seconds |
| pull_timestamp | int | Queue dequeue timestamp, in seconds |
| waiting_time | int | Time spent waiting in the queue, in milliseconds |
| alg_process_time | int | Algorithm processing time in milliseconds, including download time |
| upload_time | int | Upload time in milliseconds |
| repost_time | int | Asynchronous callback API time in milliseconds |
Fields in extra:
| Field | Type | Description |
|---|---|---|
| trans_meta | object | Transcoding-related metadata |
Fields in trans_meta:
| Field | Type | Description |
|---|---|---|
| req_meta | object[] | List of request media metadata |
| rsp_meta | object[] | List of response media metadata |
Fields in req_meta:
| Field | Type | Description |
|---|---|---|
| duration | int | Media duration in milliseconds |
| media_type | string | Media type, such as video |
Fields in rsp_meta:
| Field | Type | Description |
|---|---|---|
| data_rate | float | Data rate in bit/s |
| duration | int | Media duration in milliseconds |
| frame_num | int | Total number of frames |
| frame_rate | float | Frame rate |
| height | int | Video height |
| media_type | string | Media type, such as video |
| size | int | File size in bytes |
| width | int | Video width |
Response Examples
Successful Response Example
{
"request_id": "",
"trace_id": "",
"code": 0,
"error_code": 0,
"message": "success",
"tips": null,
"data": {
"status": 10,
"result": {
"id": "t_mt1a3i5n7bd3daeb87-14d2-40ce-a13c-744f8425cbf4",
"urls": [
"https://obs-large.mtlab.meitu.com/mtopen/4e200dc6bf574fee899cf9e0dabe27e6/d6311210-d2bb-4c17-6491-d8556cbaa134/d6311210-d2bb-4c17-6491-d8556cbaa134.mp4"
],
"parameters": {
"rsp_media_type": "url",
"version": "2.0.0"
},
"data": {
"duration": {
"alg_process_time": 32102,
"created_timestamp": 1759234231,
"pull_timestamp": 1759234234,
"repost_time": 0,
"upload_time": 879,
"waiting_time": 2754
},
"error_code": 0,
"error_msg": "success",
"extra": {},
"media_info_list": [
{
"media_data": "https://obs-large.mtlab.meitu.com/mtopen/4e200dc6bf574fee899cf9e0dabe27e6/d6311210-d2bb-4c17-6491-d8556cbaa134/d6311210-d2bb-4c17-6491-d8556cbaa134.mp4",
"media_extra": null,
"media_profiles": {
"media_data_type": "url"
}
}
],
"msg_id": "8f7ae0f1-0d5a-4f53-4439-r0072c3dc457",
"parameter": {
"rsp_media_type": "url",
"version": "2.0.0"
}
},
"msg": "success",
"msg_id": "8f7ae0f1-0d5a-4f53-4439-r0072c3dc457",
"images": [
"https://obs-large.mtlab.meitu.com/mtopen/4e200dc6bf574fee899cf9e0dabe27e6/d6311210-d2bb-4c17-6491-d8556cbaa134/d6311210-d2bb-4c17-6491-d8556cbaa134.mp4"
],
"media_info_list": [
{
"media_data": "https://obs-large.mtlab.meitu.com/mtopen/4e200dc6bf574fee899cf9e0dabe27e6/d6311210-d2bb-4c17-6491-d8556cbaa134/d6311210-d2bb-4c17-6491-d8556cbaa134.mp4",
"media_extra": null,
"media_profiles": {
"media_data_type": "url"
}
}
]
},
"progress": 1,
"predict_elapsed": 10000,
"create_time": 1759234231775,
"task_id": "t_mt1a3i5n7bd3daeb87-14d2-40ce-a13c-744f8425cbf4",
"custom_task_id": "",
"trace_id": "f396c502-83c7-4e3f-adfb-85b7a325d000",
"client_info": "",
"init_images": null
}
}Query Required Response Example
Use the query API to retrieve the result.
Response Status: 200
content-type: application/json; charset=utf-8
{
"request_id": "",
"trace_id": "",
"code": 0,
"error_code": 0,
"message": "success",
"tips": null,
"data": {
"status": 9,
"result": {
"id": "t_mt1a3i5n7b3da6d589-46b5-4f66-a0bb-8dd22f2a172e"
},
"progress": 0,
"predict_elapsed": 10000,
"create_time": 1759202368761,
"task_id": "t_mt1a3i5n7b3da6d589-46b5-4f66-a0bb-8dd22f2a172e",
"custom_task_id": "",
"trace_id": "9129a3a2-99c4-46ce-8731-0e100e2fbee7",
"client_info": "",
"init_images": null
}
}Failed Response Example
Response Status: 400
content-type: application/json; charset=utf-8
{
"request_id": "",
"trace_id": "",
"code": 26301,
"error_code": 26301,
"message": "MTLABSDK - DEPENDENCY MISSING ERROR",
"tips": null,
"data": {
"status": 2,
"result": {
"id": "t_mt1a3i5n7b62e3f245-cb93-468c-9a21-fa663656dd1d",
"code": 26301,
"data": {
"duration": {
"alg_process_time": 0,
"created_timestamp": 1759232149,
"pull_timestamp": 1759232149,
"repost_time": 0,
"upload_time": 0,
"waiting_time": 81
},
"error_code": 26301,
"error_msg": "MTLABSDK - DEPENDENCY MISSING ERROR",
"extra": {},
"media_info_list": [],
"msg_id": "ffd2e88a-2b94-47ed-63c3-r00791cbcd50",
"parameter": null
},
"msg": "MTLABSDK - DEPENDENCY MISSING ERROR",
"msg_id": "ffd2e88a-2b94-47ed-63c3-r00791cbcd50"
},
"progress": 1,
"predict_elapsed": 10000,
"create_time": 1759232149156,
"task_id": "t_mt1a3i5n7b62e3f245-cb93-468c-9a21-fa663656dd1d",
"custom_task_id": "",
"trace_id": "8d1a18ff-edae-4556-9c1a-89167249f91e",
"client_info": "",
"init_images": null
}
}General Error Codes and Messages
See API Error Codes.
SDK Examples
Python
import json
import requests
from sign_sdk import sign
def api_call_example():
key = "your_api_key"
secret = "your_api_secret"
url = "https://openapi.meitu.com/api/v1/sdk/sync/push"
method = "POST"
headers = {
"Content-Type": "application/json",
sign.HeaderHost: "openapi.meitu.com",
}
inner_params = {
"rsp_media_type": "url",
"parameter": {
"base": {
"close_thresh": 0.2,
"color_opt": True,
"distance_error": 0.024,
"distance_error_y_plus": 0.9,
"enable_enhance": True,
"enable_tune": True,
"enhance_ratio": 1.0,
"vis_ratio": 0.49,
"vis_thresh": 0.91,
"wink_opt": False,
},
"extra": {},
"version": "2.0.0",
},
}
payload = {
"task": "/v1/mtgazecorrect_v2_vid/481969",
"task_type": "formula",
"init_images": [
{
"url": "https://obs.mtlab.meitu.com/public/EtRGxLI8ulQ43pGhuZfym9aYUD2PUbMP/MTc0OTc4MDAwMA==/0281e99d-c48a-497f-5812-6ef391e200e3.mp4",
"profile": {
"media_profiles": {"media_data_type": "url"},
"version": "v1",
},
}
],
"params": json.dumps(inner_params, ensure_ascii=False),
"sync_timeout": 30,
}
body = json.dumps(payload, ensure_ascii=False)
signer = sign.Signer(key, secret)
signed_request = signer.sign(url, method, headers, body)
response = requests.Session().send(signed_request)
print(f"Status: {response.status_code}")
print(f"Response: {response.text}")
if __name__ == "__main__":
api_call_example()Go
package main
import (
"fmt"
"io"
"net/http"
"github.com/mtlab/api/signer"
)
func main() {
key := "your_api_key"
secret := "your_api_secret"
signObj := signer.NewSigner(key, secret)
url := "https://openapi.meitu.com/api/v1/sdk/sync/push"
method := http.MethodPost
headers := make(http.Header)
headers.Set(signer.HeaderHost, "openapi.meitu.com")
headers.Set("Content-Type", "application/json")
body := `{
"task": "/v1/mtgazecorrect_v2_vid/481969",
"task_type": "formula",
"init_images": [{
"url": "https://obs.mtlab.meitu.com/public/EtRGxLI8ulQ43pGhuZfym9aYUD2PUbMP/MTc0OTc4MDAwMA==/0281e99d-c48a-497f-5812-6ef391e200e3.mp4",
"profile": {
"media_profiles": {"media_data_type": "url"},
"version": "v1"
}
}],
"params": "{\"rsp_media_type\":\"url\",\"parameter\":{\"base\":{\"close_thresh\":0.2,\"color_opt\":true,\"distance_error\":0.024,\"distance_error_y_plus\":0.9,\"enable_enhance\":true,\"enable_tune\":true,\"enhance_ratio\":1.0,\"vis_ratio\":0.49,\"vis_thresh\":0.91,\"wink_opt\":false},\"extra\":{},\"version\":\"2.0.0\"}}",
"sync_timeout": 30
}`
req, err := signObj.Sign(url, method, headers, body)
if err != nil {
fmt.Println("Failed to sign request:", err)
return
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
fmt.Println("Failed to send request:", err)
return
}
defer resp.Body.Close()
responseBody, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("Read response failed:", err)
return
}
fmt.Println("Response:", resp.StatusCode, string(responseBody))
}PHP
<?php
require 'signer.php';
$key = 'your_api_key';
$secret = 'your_api_secret';
$signer = new Signer($key, $secret);
$url = 'https://openapi.meitu.com/api/v1/sdk/sync/push';
$method = 'POST';
$headers = ['Content-Type' => 'application/json'];
$innerParams = json_encode([
'rsp_media_type' => 'url',
'parameter' => [
'base' => [
'close_thresh' => 0.2,
'color_opt' => true,
'distance_error' => 0.024,
'distance_error_y_plus' => 0.9,
'enable_enhance' => true,
'enable_tune' => true,
'enhance_ratio' => 1.0,
'vis_ratio' => 0.49,
'vis_thresh' => 0.91,
'wink_opt' => false,
],
'extra' => [],
'version' => '2.0.0',
],
]);
$body = json_encode([
'task' => '/v1/mtgazecorrect_v2_vid/481969',
'task_type' => 'formula',
'init_images' => [
[
'url' => 'https://obs.mtlab.meitu.com/public/EtRGxLI8ulQ43pGhuZfym9aYUD2PUbMP/MTc0OTc4MDAwMA==/0281e99d-c48a-497f-5812-6ef391e200e3.mp4',
'profile' => [
'media_profiles' => ['media_data_type' => 'url'],
'version' => 'v1',
],
],
],
'params' => $innerParams,
'sync_timeout' => 30,
]);
$curl = $signer->sign($url, $method, $headers, $body);
$response = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if ($status === 0) {
echo 'Error: ' . curl_error($curl);
} else {
echo "Status: {$status}\n";
echo "Response: {$response}\n";
}
curl_close($curl);
?>Java
package com.meitu.openai.common;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
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 {
Signer signer = new Signer("your_api_key", "your_api_secret");
String url = "https://openapi.meitu.com/api/v1/sdk/sync/push";
String method = "POST";
Map<String, String> headers = new HashMap<>();
headers.put("Content-Type", "application/json");
headers.put(Signer.HeaderHost, "openapi.meitu.com");
String body = "{\n" +
" \"task\": \"/v1/mtgazecorrect_v2_vid/481969\",\n" +
" \"task_type\": \"formula\",\n" +
" \"init_images\": [{\n" +
" \"url\": \"https://obs.mtlab.meitu.com/public/EtRGxLI8ulQ43pGhuZfym9aYUD2PUbMP/MTc0OTc4MDAwMA==/0281e99d-c48a-497f-5812-6ef391e200e3.mp4\",\n" +
" \"profile\": {\n" +
" \"media_profiles\": {\"media_data_type\": \"url\"},\n" +
" \"version\": \"v1\"\n" +
" }\n" +
" }],\n" +
" \"params\": \"{\\\"rsp_media_type\\\":\\\"url\\\",\\\"parameter\\\":{\\\"base\\\":{\\\"close_thresh\\\":0.2,\\\"color_opt\\\":true,\\\"distance_error\\\":0.024,\\\"distance_error_y_plus\\\":0.9,\\\"enable_enhance\\\":true,\\\"enable_tune\\\":true,\\\"enhance_ratio\\\":1.0,\\\"vis_ratio\\\":0.49,\\\"vis_thresh\\\":0.91,\\\"wink_opt\\\":false},\\\"extra\\\":{},\\\"version\\\":\\\"2.0.0\\\"}}\",\n" +
" \"sync_timeout\": 30\n" +
"}";
Map<String, String> signedHeaders = signer.sign(url, method, headers, body);
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.setRequestMethod(method);
for (Map.Entry<String, String> entry : signedHeaders.entrySet()) {
connection.setRequestProperty(entry.getKey(), entry.getValue());
}
connection.setDoOutput(true);
connection.getOutputStream().write(body.getBytes(StandardCharsets.UTF_8));
int status = connection.getResponseCode();
InputStream stream = status >= 400
? connection.getErrorStream()
: connection.getInputStream();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream))) {
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
System.out.println("Response: " + status + " " + response);
}
}
}