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): formula

Method

POST

Content-Type: application/json

Authentication

Open Platform API Signature

Request Parameters

RequiredParameterTypeDescription
YesparamsstringAlgorithm parameters (JSON string)
Yesinit_imagesobject[]List of media files
YestaskstringFixed value: /v1/mtgazecorrect_v2_vid/481969
Yestask_typestringFixed value: formula
Nosync_timeoutintDefault: 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:

RequiredParameterTypeDescription
YesurlstringMedia URL or base64 data
YesprofileobjectMedia attribute information

profile attribute information:

RequiredParameterTypeDescription
Yesmedia_profilesobjectMedia properties
YesversionstringFixed value: v1

media_profiles media properties:

RequiredParameterTypeDescription
Yesmedia_data_typestringurl indicates a URL; types such as jpg indicate base64 data

The params inference parameter is a JSON string with the following structure:

RequiredFieldTypeDescription
Norsp_media_typestringDefault: url; jpg indicates base64 data
YesparameterobjectCore 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.

RequiredParameterTypeDescription
Not specified in the source documentbaseobjectEye contact correction parameter object; see the table below for its structure
Not specified in the source documentextraobjectExtended parameters; an empty object in the original request example
Not specified in the source documentversionstringValue in the original request example: 2.0.0

Details of the base algorithm parameters:

RequiredParameterTypeDescription
Noclose_threshfloatEye-closure threshold; default: 0.2
Novis_ratiofloatVisible landmark ratio; default: 0.49
Novis_threshfloatVisible landmark confidence threshold; default: 0.91
Noenhance_ratiofloatEnhancement factor; default: 1.0
Nodistance_errorfloatEyeball position determination threshold; default: 0.024
Not specified in the source documentdistance_error_y_plusfloatValue in the original request example: 0.9; meaning, default value, and required status are not specified in the source document
Nocolor_optboolLocal color correction; default: true
Noenable_tuneboolLocal fine-tuning; default: true
Nowink_optboolBlink optimization; default: false
Noenable_enhanceboolDetail 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.
FieldTypeDescription
request_idstringRequest identifier
trace_idstringTrace identifier
codeintBusiness status code; 0 indicates that the request was accepted successfully
error_codeintError code; 0 indicates success
messagestringBusiness message
tipsanyAdditional information; may be null
dataobjectTask status and algorithm result

Fields in data:

FieldTypeDescription
statusintStatus code: -1, task not found; 0, created successfully; 1, processing; 2, failed; 9, timed out—use the query API; 10, succeeded
resultobjectAlgorithm result
progressnumberTask progress (0–100); 0 indicates not started or newly created
predict_elapsedintEstimated processing time in milliseconds
create_timeint64Creation timestamp in milliseconds
task_idstringTask ID
custom_task_idstringClient-defined task ID
trace_idstringTrace identifier within data
client_infostringClient information
init_imagesobject[]/nullEchoed input media

Fields in result:

FieldTypeDescription
idstringTask ID; the original field table specifies int, while the original response example uses string
urlsstring[]List of result media URLs
parametersobjectTask parameter information
dataobjectTask result information; see the table below for its structure
predict_elapsedintEstimated processing time; the source document specifies seconds
msgstringThe 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_idstringUnique string used to distinguish each request
imagesstring[]List of result media URLs
media_info_listobject[]List of media result information
codeintAlgorithm error code; 26301 in the failure example

Fields in result.data:

FieldTypeDescription
media_info_listobject[]Each item contains media information
parameterobject/nullReturned information type identifier
error_codeintReturned error code; 0 indicates success
error_msgstringReturned error message
msg_idstringUnique request identifier; the source document states that this identifier and the algorithm result are retained for only 24 hours
durationobjectDetailed timing information
extraobjectExtended information

Fields in media_info_list:

FieldTypeDescription
media_datastringThe 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_extranullReserved field; currently null
media_profilesobjectAdditional media information; see the table below

Fields in media_profiles:

FieldTypeDescription
media_data_fpsfloatVideo frame rate
media_data_heightintVideo height
media_data_sizeintVideo data size in bytes
media_data_typestringType of the returned media_data; for example, url indicates an address
media_data_widthintVideo width

Fields in duration:

FieldTypeDescription
created_timestampintTimestamp when the algorithm receives the request, in seconds
pull_timestampintQueue dequeue timestamp, in seconds
waiting_timeintTime spent waiting in the queue, in milliseconds
alg_process_timeintAlgorithm processing time in milliseconds, including download time
upload_timeintUpload time in milliseconds
repost_timeintAsynchronous callback API time in milliseconds

Fields in extra:

FieldTypeDescription
trans_metaobjectTranscoding-related metadata

Fields in trans_meta:

FieldTypeDescription
req_metaobject[]List of request media metadata
rsp_metaobject[]List of response media metadata

Fields in req_meta:

FieldTypeDescription
durationintMedia duration in milliseconds
media_typestringMedia type, such as video

Fields in rsp_meta:

FieldTypeDescription
data_ratefloatData rate in bit/s
durationintMedia duration in milliseconds
frame_numintTotal number of frames
frame_ratefloatFrame rate
heightintVideo height
media_typestringMedia type, such as video
sizeintFile size in bytes
widthintVideo 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);
        }
    }
}