理解下面程序，改成fastapi版本，并且支持跨域：import os
import time
import pdb
import re

import gradio as gr
import spaces
import numpy as np
import sys
import subprocess

from huggingface_hub import snapshot_download
import requests

import argparse
import os
from omegaconf import OmegaConf
import numpy as np
import cv2
import torch
import glob
import pickle
from tqdm import tqdm
import copy
from argparse import Namespace
import shutil
import gdown
import imageio
import ffmpeg
from moviepy.editor import *

ProjectDir = os.path.abspath(os.path.dirname(__file__))
CheckpointsDir = os.path.join(ProjectDir, "models")

css = """
#warning {background-color: transparent}
"""

def print_directory_contents(path):
    for child in os.listdir(path):
        child_path = os.path.join(path, child)
        if os.path.isdir(child_path):
            print(child_path)

def download_model():
    if not os.path.exists(CheckpointsDir):
        os.makedirs(CheckpointsDir)
        print("Checkpoint Not Downloaded, start downloading...")
        tic = time.time()
        snapshot_download(
            repo_id="TMElyralab/MuseTalk",
            local_dir=CheckpointsDir,
            max_workers=8,
            local_dir_use_symlinks=True,
        )
        # weight
        os.makedirs(f"{CheckpointsDir}/sd-vae-ft-mse/")
        snapshot_download(
            repo_id="stabilityai/sd-vae-ft-mse",
            local_dir=CheckpointsDir+'/sd-vae-ft-mse',
            max_workers=8,
            local_dir_use_symlinks=True,
        )
        #dwpose
        os.makedirs(f"{CheckpointsDir}/dwpose/")
        snapshot_download(
            repo_id="yzd-v/DWPose",
            local_dir=CheckpointsDir+'/dwpose',
            max_workers=8,
            local_dir_use_symlinks=True,
        )
        #vae
        url = "https://openaipublic.azureedge.net/main/whisper/models/65147644a518d12f04e32d6f3b26facc3f8dd46e5390956a9424a650c0ce22b9/tiny.pt"
        response = requests.get(url)
        # 确保请求成功
        if response.status_code == 200:
            # 指定文件保存的位置
            file_path = f"{CheckpointsDir}/whisper/tiny.pt"
            os.makedirs(f"{CheckpointsDir}/whisper/")
            # 将文件内容写入指定位置
            with open(file_path, "wb") as f:
                f.write(response.content)
        else:
            print(f"请求失败，状态码：{response.status_code}")
        #gdown face parse
        url = "https://drive.google.com/uc?id=154JgKpzCPW82qINcVieuPH3fZ2e0P812"
        os.makedirs(f"{CheckpointsDir}/face-parse-bisent/")
        file_path = f"{CheckpointsDir}/face-parse-bisent/79999_iter.pth"
        gdown.download(url, file_path, quiet=False)
        #resnet
        url = "https://download.pytorch.org/models/resnet18-5c106cde.pth"
        response = requests.get(url)
        # 确保请求成功
        if response.status_code == 200:
            # 指定文件保存的位置
            file_path = f"{CheckpointsDir}/face-parse-bisent/resnet18-5c106cde.pth"
            # 将文件内容写入指定位置
            with open(file_path, "wb") as f:
                f.write(response.content)
        else:
            print(f"请求失败，状态码：{response.status_code}")


        toc = time.time()

        print(f"download cost {toc-tic} seconds")
        print_directory_contents(CheckpointsDir)

    else:
        print("Already download the model.")





download_model()  # for huggingface deployment.


from musetalk.utils.utils import get_file_type,get_video_fps,datagen
from musetalk.utils.preprocessing import get_landmark_and_bbox,read_imgs,coord_placeholder,get_bbox_range
from musetalk.utils.blending import get_image
from musetalk.utils.utils import load_all_model


# load model weights
audio_processor,vae,unet,pe  = load_all_model()
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
timesteps = torch.tensor([0], device=device)



def trim_video_to_audio_length(video_path, audio_path, output_path):
    if get_file_type(video_path)=="video":
        # 获取视频时间长度
        cmd = ['ffprobe', '-v', 'error', '-show_entries', 'format=duration', '-of', 'default=noprint_wrappers=1:nokey=1', video_path]
        video_duration = float(subprocess.check_output(cmd).decode().strip())
    
        # 获取音频时间长度
        cmd = ['ffprobe', '-v', 'error', '-show_entries', 'format=duration', '-of', 'default=noprint_wrappers=1:nokey=1', audio_path]
        audio_duration = float(subprocess.check_output(cmd).decode().strip())
    
        # 如果音频时间长度小于视频时间长度,则裁剪视频
        if audio_duration < video_duration:
            cmd = ['ffmpeg','-y', '-i', video_path, '-t', str(audio_duration), '-c', 'copy', output_path]
            subprocess.call(cmd)
            print(f"视频已裁切至与音频相同的时间长度: {audio_duration}秒")
        else:
            output_path = video_path

    return output_path

def get_trimmed_video_path(video_path):
    # 提取视频文件的目录部分
    video_dir = os.path.dirname(video_path)
    
    # 提取视频文件的名称部分(不包括扩展名)
    video_name = os.path.splitext(os.path.basename(video_path))[0]
    
    # 获取视频文件的扩展名
    video_ext = os.path.splitext(video_path)[1]
    
    # 生成裁剪后的视频文件名
    trimmed_video_name = f"{video_name}_trim{video_ext}"
    
    # 拼接裁剪后的视频文件的完整路径
    trimmed_video_path = os.path.join(video_dir, trimmed_video_name)
    
    return trimmed_video_path


def generate_batch_tasks(media_dir, yaml_path):
    # 支持的视频/图片文件扩展名
    video_extensions = ['.jpg', '.jpeg', '.png', '.bmp', '.tif', '.tiff', '.avi', '.mp4', '.mov', '.flv', '.mkv']
    
    # 支持的音频文件扩展名
    audio_extensions = ['.wav', '.mp3']

    # 创建一个空的配置字典
    config = {}

    # 遍历媒体目录下的文件
    for filename in os.listdir(media_dir):
        # 检查文件扩展名是否在支持的扩展名列表中
        if os.path.splitext(filename)[1].lower() in video_extensions:
            # 构建视频/图片文件的完整路径
            video_path = os.path.join(media_dir, filename)
            
            # 根据视频/图片文件名找到对应的音频文件
            audio_filename = os.path.splitext(filename)[0]
            for ext in audio_extensions:
                audio_path = os.path.join(media_dir, audio_filename + ext)
                
                # 检查音频文件是否存在
                if os.path.exists(audio_path):
                    # 创建一个新的任务配置
                    task_config = {
                        "video_path": video_path,
                        "audio_path": audio_path
                    }
                    
                    # 将任务配置添加到配置字典中
                    task_name = f"task_{len(config)}"
                    config[task_name] = task_config
                    
                    # 找到匹配的音频文件后，跳出循环
                    break

    # 将配置字典转换为OmegaConf配置对象
    config = OmegaConf.create(config)

    # 清空文件内容
    with open(yaml_path, "w") as file:
        file.write("")

    # 将配置写入YAML文件
    with open(yaml_path, "a") as file:
        OmegaConf.save(config, file)

    print(f"配置已写入到 {yaml_path}")




@torch.no_grad()
def inference(audio_path,video_path,bbox_shift,batch_size_args,use_saved_coord_args,trim_video,css=css):
    start_time=time.time()
    args_dict={"result_dir":'/root/autodl-tmp/results/output', "fps":25, "batch_size":4, "output_vid_name":'', "use_saved_coord":"store_true"}#same with inferenece script
    args = Namespace(**args_dict)

    # 如果开始裁切视频
    if trim_video:
        video_path = trim_video_to_audio_length(video_path,audio_path,get_trimmed_video_path(video_path))
        print("裁切后的视频路径：",video_path)

    input_basename = os.path.basename(video_path).split('.')[0]
    audio_basename  = os.path.basename(audio_path).split('.')[0]
    output_basename = f"{input_basename}_{audio_basename}"
    result_img_save_path = os.path.join(args.result_dir, output_basename) # related to video & audio inputs
    input_img_save_path = os.path.join(args.result_dir, input_basename) # related to video & audio inputs
    crop_coord_save_path = os.path.join(input_img_save_path, input_basename+".pkl") # only related to video input
    print("crop_coord_save_path ",crop_coord_save_path)
    os.makedirs(result_img_save_path,exist_ok =True)

    if args.output_vid_name == "":
        output_vid_name = os.path.join(args.result_dir, output_basename+".mp4")
    else:
        output_vid_name = os.path.join(args.result_dir, args.output_vid_name)
    ############################################## extract frames from source video ##############################################
    if get_file_type(video_path)=="video":
        save_dir_full = os.path.join(args.result_dir, input_basename)
        os.makedirs(save_dir_full,exist_ok = True)
        cmd = f"ffmpeg -v fatal -i {video_path} -start_number 0 {save_dir_full}/%08d.png"
        os.system(cmd)
        input_img_list = sorted(glob.glob(os.path.join(save_dir_full, '*.[jpJP][pnPN]*[gG]')))
        fps = get_video_fps(video_path)
    elif get_file_type(video_path)=="image":
        input_img_list = [video_path, ]
        fps = args.fps
    elif os.path.isdir(video_path):  # input img folder
        input_img_list = glob.glob(os.path.join(video_path, '*.[jpJP][pnPN]*[gG]'))
        input_img_list = sorted(input_img_list, key=lambda x: int(os.path.splitext(os.path.basename(x))[0]))
        fps = args.fps
    else:
        raise ValueError(f"{video_path} should be a video file, an image file or a directory of images")
    #print(input_img_list)
    ############################################## extract audio feature ##############################################
    whisper_feature = audio_processor.audio2feat(audio_path)
    whisper_chunks = audio_processor.feature2chunks(feature_array=whisper_feature,fps=fps)
    ############################################## preprocess input image  ##############################################
    bbox_shift_text = None
    if os.path.exists(crop_coord_save_path) and use_saved_coord_args:
        print("使用提取的landmark")
        with open(crop_coord_save_path,'rb') as f:
            coord_list = pickle.load(f)
        frame_list = read_imgs(input_img_list)
    else:
        print("重新提取landmark，很耗时")
        coord_list, frame_list,bbox_shift_text = get_landmark_and_bbox(input_img_list, bbox_shift)
        with open(crop_coord_save_path, 'wb') as f:
            pickle.dump(coord_list, f)
    #bbox_shift_text=get_bbox_range(input_img_list, bbox_shift)        
    i = 0
    input_latent_list = []
    for bbox, frame in zip(coord_list, frame_list):
        if bbox == coord_placeholder:
            continue
        x1, y1, x2, y2 = bbox
        crop_frame = frame[y1:y2, x1:x2]
        crop_frame = cv2.resize(crop_frame,(256,256),interpolation = cv2.INTER_LANCZOS4)
        latents = vae.get_latents_for_unet(crop_frame)
        input_latent_list.append(latents)

    # to smooth the first and the last frame
    frame_list_cycle = frame_list + frame_list[::-1]
    coord_list_cycle = coord_list + coord_list[::-1]
    input_latent_list_cycle = input_latent_list + input_latent_list[::-1]
    ############################################## inference batch by batch ##############################################
    print("start inference")
    video_num = len(whisper_chunks)
    batch_size = batch_size_args
    gen = datagen(whisper_chunks,input_latent_list_cycle,batch_size)
    res_frame_list = []
    for i, (whisper_batch,latent_batch) in enumerate(tqdm(gen,total=int(np.ceil(float(video_num)/batch_size)))):
        
        tensor_list = [torch.FloatTensor(arr) for arr in whisper_batch]
        audio_feature_batch = torch.stack(tensor_list).to(unet.device) # torch, B, 5*N,384
        audio_feature_batch = pe(audio_feature_batch)
        
        pred_latents = unet.model(latent_batch, timesteps, encoder_hidden_states=audio_feature_batch).sample
        recon = vae.decode_latents(pred_latents)
        for res_frame in recon:
            res_frame_list.append(res_frame)
            
    ############################################## pad to full image ##############################################
    print("pad talking image to original video")
    for i, res_frame in enumerate(tqdm(res_frame_list)):
        bbox = coord_list_cycle[i%(len(coord_list_cycle))]
        ori_frame = copy.deepcopy(frame_list_cycle[i%(len(frame_list_cycle))])
        x1, y1, x2, y2 = bbox
        try:
            res_frame = cv2.resize(res_frame.astype(np.uint8),(x2-x1,y2-y1))
        except:
#             print(bbox)
            continue
        
        combine_frame = get_image(ori_frame,res_frame,bbox)
        cv2.imwrite(f"{result_img_save_path}/{str(i).zfill(8)}.png",combine_frame)
    cmd_img2video = f"ffmpeg -y -v warning -r {fps} -f image2 -i {result_img_save_path}/%08d.png -vcodec libx264 -vf format=rgb24,scale=out_color_matrix=bt709,format=yuv420p -crf 18 temp.mp4"
    print(cmd_img2video)
    os.system(cmd_img2video)
    
    cmd_combine_audio = f"ffmpeg -y -v warning -i {audio_path} -i temp.mp4 {output_vid_name}"
    print(cmd_combine_audio)
    os.system(cmd_combine_audio)
    
    os.remove("temp.mp4")
    shutil.rmtree(result_img_save_path)
    print(f"result is save to {output_vid_name}")
    end_time=time.time()
    elapsed_time = end_time - start_time
    print(f"代码块执行耗时：{elapsed_time}秒")
    gr.Info(f"推理完成，执行耗时：{elapsed_time}秒")
    return output_vid_name,bbox_shift_text



@torch.no_grad()
def batch_inference(audio_path,video_path,bbox_shift,batch_size_args,use_saved_coord_args,trim_video,batch_file,css=css):
    start_time=time.time()    
    args_dict={"result_dir":'/root/autodl-tmp/results/output_batch', "fps":25, "batch_size":8, "output_vid_name":'', "use_saved_coord":"store_true"}#same with inferenece script
    args = Namespace(**args_dict)

    batch_basename = os.path.basename(batch_file).split('.')[0]
    args.result_dir = f"/root/autodl-tmp/results/{batch_basename}_output_batch"
    # 指定要写入的YAML文件路径
    yaml_path = f"configs/inference/{batch_basename}_task.yaml"
    # 调用函数生成批量任务配置
    generate_batch_tasks(batch_file, yaml_path)

    inference_config = OmegaConf.load(yaml_path)
    print(inference_config)
    for task_id in inference_config:
        video_path = inference_config[task_id]["video_path"]
        audio_path = inference_config[task_id]["audio_path"]

        # 如果开始裁切视频
        if trim_video:
            video_path = trim_video_to_audio_length(video_path,audio_path,get_trimmed_video_path(video_path))
            print("裁切后的视频路径：",video_path)

        input_basename = os.path.basename(video_path).split('.')[0]
        audio_basename  = os.path.basename(audio_path).split('.')[0]
        output_basename = f"{input_basename}_{audio_basename}"
        result_img_save_path = os.path.join(args.result_dir, output_basename) # related to video & audio inputs
        input_img_save_path = os.path.join(args.result_dir, input_basename) # related to video & audio inputs
        crop_coord_save_path = os.path.join(input_img_save_path, input_basename+".pkl") # only related to video input
        print("crop_coord_save_path ",crop_coord_save_path)
        os.makedirs(result_img_save_path,exist_ok =True)
    
        if args.output_vid_name == "":
            output_vid_name = os.path.join(args.result_dir, output_basename+".mp4")
        else:
            output_vid_name = os.path.join(args.result_dir, args.output_vid_name)
        ############################################## extract frames from source video ##############################################
        if get_file_type(video_path)=="video":
            save_dir_full = os.path.join(args.result_dir, input_basename)
            os.makedirs(save_dir_full,exist_ok = True)
            cmd = f"ffmpeg -v fatal -i {video_path} -start_number 0 {save_dir_full}/%08d.png"
            os.system(cmd)
            input_img_list = sorted(glob.glob(os.path.join(save_dir_full, '*.[jpJP][pnPN]*[gG]')))
            fps = get_video_fps(video_path)
        elif get_file_type(video_path)=="image":
            input_img_list = [video_path, ]
            fps = args.fps
        elif os.path.isdir(video_path):  # input img folder
            input_img_list = glob.glob(os.path.join(video_path, '*.[jpJP][pnPN]*[gG]'))
            input_img_list = sorted(input_img_list, key=lambda x: int(os.path.splitext(os.path.basename(x))[0]))
            fps = args.fps
        else:
            raise ValueError(f"{video_path} should be a video file, an image file or a directory of images")
        #print(input_img_list)
        ############################################## extract audio feature ##############################################
        whisper_feature = audio_processor.audio2feat(audio_path)
        whisper_chunks = audio_processor.feature2chunks(feature_array=whisper_feature,fps=fps)
        ############################################## preprocess input image  ##############################################
        bbox_shift_text = None
        if os.path.exists(crop_coord_save_path) and use_saved_coord_args:
            print("使用提取的landmark")
            with open(crop_coord_save_path,'rb') as f:
                coord_list = pickle.load(f)
            frame_list = read_imgs(input_img_list)
        else:
            print("重新提取landmark，很耗时")
            coord_list, frame_list,bbox_shift_text = get_landmark_and_bbox(input_img_list, bbox_shift)
            with open(crop_coord_save_path, 'wb') as f:
                pickle.dump(coord_list, f)
        #bbox_shift_text=get_bbox_range(input_img_list, bbox_shift)        
        i = 0
        input_latent_list = []
        for bbox, frame in zip(coord_list, frame_list):
            if bbox == coord_placeholder:
                continue
            x1, y1, x2, y2 = bbox
            crop_frame = frame[y1:y2, x1:x2]
            crop_frame = cv2.resize(crop_frame,(256,256),interpolation = cv2.INTER_LANCZOS4)
            latents = vae.get_latents_for_unet(crop_frame)
            input_latent_list.append(latents)
    
        # to smooth the first and the last frame
        frame_list_cycle = frame_list + frame_list[::-1]
        coord_list_cycle = coord_list + coord_list[::-1]
        input_latent_list_cycle = input_latent_list + input_latent_list[::-1]
        ############################################## inference batch by batch ##############################################
        print("start inference")
        video_num = len(whisper_chunks)
        batch_size = batch_size_args
        gen = datagen(whisper_chunks,input_latent_list_cycle,batch_size)
        res_frame_list = []
        for i, (whisper_batch,latent_batch) in enumerate(tqdm(gen,total=int(np.ceil(float(video_num)/batch_size)))):
            
            tensor_list = [torch.FloatTensor(arr) for arr in whisper_batch]
            audio_feature_batch = torch.stack(tensor_list).to(unet.device) # torch, B, 5*N,384
            audio_feature_batch = pe(audio_feature_batch)
            
            pred_latents = unet.model(latent_batch, timesteps, encoder_hidden_states=audio_feature_batch).sample
            recon = vae.decode_latents(pred_latents)
            for res_frame in recon:
                res_frame_list.append(res_frame)
                
        ############################################## pad to full image ##############################################
        print("pad talking image to original video")
        for i, res_frame in enumerate(tqdm(res_frame_list)):
            bbox = coord_list_cycle[i%(len(coord_list_cycle))]
            ori_frame = copy.deepcopy(frame_list_cycle[i%(len(frame_list_cycle))])
            x1, y1, x2, y2 = bbox
            try:
                res_frame = cv2.resize(res_frame.astype(np.uint8),(x2-x1,y2-y1))
            except:
    #             print(bbox)
                continue
            
            combine_frame = get_image(ori_frame,res_frame,bbox)
            cv2.imwrite(f"{result_img_save_path}/{str(i).zfill(8)}.png",combine_frame)
        cmd_img2video = f"ffmpeg -y -v warning -r {fps} -f image2 -i {result_img_save_path}/%08d.png -vcodec libx264 -vf format=rgb24,scale=out_color_matrix=bt709,format=yuv420p -crf 18 temp.mp4"
        print(cmd_img2video)
        os.system(cmd_img2video)
        
        cmd_combine_audio = f"ffmpeg -y -v warning -i {audio_path} -i temp.mp4 {output_vid_name}"
        print(cmd_combine_audio)
        os.system(cmd_combine_audio)
        
        os.remove("temp.mp4")
        shutil.rmtree(result_img_save_path)
        print(f"result is save to {output_vid_name}")
        end_time=time.time()
        elapsed_time = end_time - start_time
        print(f"代码块执行耗时：{elapsed_time}秒")
        gr.Info(f"第{task_id}个视频推理完成，执行耗时：{elapsed_time}秒")
    gr.Info("批量生成完毕！")
    return output_vid_name



#css = """#input_img {max-width: 1024px !important} #output_vid {max-width: 1024px; max-height: 576px}"""


with gr.Blocks(analytics_enabled=False,css=css) as demo:
    #gr.Markdown("\
           # <div align='center'> <h2> ai 对口型大模型</span> </h2> \
           # <a style='font-size:18px;color: #a0a0a0' href='https://www.codewithgpu.com/u/AIGC666'> 科哥更多Autodl镜像点这里 </a>  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
           # </div>")
   
    with gr.Column(elem_id="warning"):
         with gr.Tabs('',elem_id="warning"):
            with gr.TabItem(elem_id="warning"):
                # 视频上传和显示的列
                with gr.Row(variant='panel',elem_id="warning"):
                    with gr.Column(variant='panel'):
                        #video = gr.Video(label="Reference Video",sources=['upload'])
                       
                        audio = gr.Audio(label="上传驱动音频", type="filepath")
                        video = gr.Video(label="上传原始视频", height = 400, width = 1280, elem_id="input_video", interactive=True)
                    output_video = gr.Video(label="显示生成视频", elem_id="output_video", interactive=False)   
                    # 指定要遍历的文件夹路径
                    #video_folder = "data/video"
                    # 获取文件夹下所有的视频文件
                    #video_files = [os.path.join(video_folder, f) for f in os.listdir(video_folder) if f.lower().endswith((".mp4", ".avi", ".mov"))]
                    #example = gr.Examples(
                            #label="视频例子",
                            #inputs=video,
                            #examples_per_page=10,
                            #examples=video_files,)

                    # 所有操作按钮的列
                    with gr.Column(visible=False,variant='panel'):
                        #trim_video=gr.Checkbox(label="开启", info="视频裁切至与音频相同的时间长度")
                        #saved_coord=gr.Checkbox(label="开启", info="是否使用保存的坐标提升效率")
                        #batch_size = gr.Number(label="batch_size 根据显存调节", value=8)
                        #bbox_shift = gr.Number(label="调节嘴型张合度", value=0)
                        #bbox_shift_scale = gr.Textbox(label="可调节嘴型张合度范围[n,n]", value="",interactive=False)
                        #batch_file = gr.Textbox(label="输入需要批量处理的路径 data/xxx", value="")
                        #btn = gr.Button("开始推理")
                        #batch_btn = gr.Button("批量推理")
                    
                        
                        
                    
                        trim_video=gr.Checkbox(label="开启", info="视频裁切至与音频相同的时间长度")
                        saved_coord=gr.Checkbox(label="开启", info="是否使用保存的坐标提升效率")
                        batch_size = gr.Number(label="batch_size 根据显存调节", value=8)
                        bbox_shift = gr.Number(label="调节嘴型张合度", value=0)
                        bbox_shift_scale = gr.Textbox(label="可调节嘴型张合度范围[n,n]", value="",interactive=False)
                        batch_file = gr.Textbox(label="输入需要批量处理的路径 data/xxx", value="")
                    with gr.Column(variant='panel'):
                        btn = gr.Button("生成视频")
            
                        
                with gr.Row(variant='panel',elem_id="warning"):
                    with gr.Column(variant='panel'):
                        # 指定要遍历的文件夹路径
                        audio_folder = "data/audio"
                        # 获取文件夹下所有的视频文件
                        audio_files = [os.path.join(audio_folder, f) for f in os.listdir(audio_folder) if f.lower().endswith((".wav", ".mp3"))]
                     
                        
                        # 指定要遍历的文件夹路径
                        video_folder = "data/video"
                        # 获取文件夹下所有的视频文件
                        video_files = [os.path.join(video_folder, f) for f in os.listdir(video_folder) if f.lower().endswith((".mp4", ".avi", ".mov"))]
                        
                        
                        # 指定要遍历的文件夹路径
                        image_folder = "data/image"
                        # 获取文件夹下所有的视频文件
                        image_files = [os.path.join(image_folder, f) for f in os.listdir(image_folder) if f.lower().endswith((".png", ".jpg", ".jpeg", ".bmp"))]
                       
            
            #video.change(
                #fn=check_video, inputs=[video], outputs=[video]
            #)
            btn.click(
                fn=inference,
                inputs=[
                    audio,
                    video,
                    bbox_shift,  
                    batch_size,
                    saved_coord,
                    trim_video,
                ],
                outputs=[output_video,bbox_shift_scale]
            )

# Set the IP and port
ip_address = "127.0.0.1"  # Replace with your desired IP address
port_number = 6006  # Replace with your desired port number


demo.queue().launch(
    share=False , debug=True, server_name=ip_address, server_port=port_number
)