import os
import sys
import cv2
import time
import numpy as np
import subprocess
import sounddevice as sd
from scipy.io import wavfile
from scipy.signal import resample
from PyQt6 import QtCore, QtGui, QtWidgets
from PyQt6.QtCore import pyqtSignal, QThread
from talkingface.audio_model import AudioModel
from talkingface.render_model import RenderModel
import queue
import threading  # 添加线程模块导入
import warnings
from cryptography.utils import CryptographyDeprecationWarning

# 忽略 CryptographyDeprecationWarning
warnings.filterwarnings("ignore", category=CryptographyDeprecationWarning)

class VideoWindow(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Video Window")
        self.setGeometry(100, 100, 800, 600)
        
        # 使用布局使界面更具可扩展性
        self.layout = QtWidgets.QVBoxLayout()
        self.setLayout(self.layout)
        
        self.label = QtWidgets.QLabel(self)
        self.label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
        self.label.setScaledContents(True)  # 自动缩放图像以适应 QLabel 大小
        self.layout.addWidget(self.label)
        
        # 尝试加载静态图像 '0.jpg' 作为初始画面
        image_path = "0.jpg"  # 确保 '0.jpg' 位于当前工作目录或提供完整路径
        test_image = cv2.imread(image_path)
        
        if test_image is not None:
            # 调整图像大小以匹配 QLabel 的尺寸 (800x600)
            test_image = cv2.resize(test_image, (800, 600))
            print(f"Loaded initial image from {image_path}.")
        else:
            # 如果加载失败，使用默认的绿色背景
            print(f"Failed to load {image_path}. Using green screen instead.")
            test_image = np.zeros((600, 800, 3), dtype=np.uint8)
            test_image[:] = (0, 255, 0)  # 绿色背景
        
        self.update_frame(test_image)

    @QtCore.pyqtSlot(np.ndarray)
    def update_frame(self, frame):
        if frame is None:
            print("Received None frame")
            return
        
        if frame.size == 0:
            print("Received empty frame")
            return
        
        try:
            # 确保传入的帧是 BGR 格式，为 OpenCV 默认格式
            if len(frame.shape) == 3 and frame.shape[2] == 3:
                frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            else:
                frame_rgb = frame.copy()
            
            height, width, channel = frame_rgb.shape
            bytes_per_line = 3 * width
            
            # 创建 QImage 对象
            q_image = QtGui.QImage(
                frame_rgb.data,
                width,
                height,
                bytes_per_line,
                QtGui.QImage.Format.Format_RGB888
            )
            
            # 转换为 QPixmap 并设置到 QLabel
            pixmap = QtGui.QPixmap.fromImage(q_image)
            self.label.setPixmap(pixmap)
        except Exception as e:
            print(f"Error updating frame: {e}")

class VideoAudioWorker(QThread):
    frame_signal = pyqtSignal(np.ndarray)  # 当新的帧准备好时发射信号

    def __init__(self, parent=None):
        super().__init__(parent)
        self._run_flag = True
        
        # 初始化模型
        self.audioModel = AudioModel()
        try:
            self.audioModel.loadModel("checkpoint/audio.pkl")
            print("AudioModel loaded successfully.")
        except Exception as e:
            print(f"Failed to load AudioModel: {e}")
            self._run_flag = False
        
        self.renderModel = RenderModel()
        try:
            self.renderModel.loadModel("checkpoint/render.pth")
            print("RenderModel loaded successfully.")
        except Exception as e:
            print(f"Failed to load RenderModel: {e}")
            self._run_flag = False
        
        # 设置测试视频和关键点
        self.test_video = "test3"
        self.pkl_path = f"video_data/{self.test_video}/keypoint_rotate.pkl"
        self.video_path = f"video_data/{self.test_video}/circle.mp4"
        try:
            self.renderModel.reset_charactor(self.video_path, self.pkl_path)
            print("Character reset successfully.")
        except Exception as e:
            print(f"Failed to reset character: {e}")
            self._run_flag = False
        
        # 音频处理
        self.wavpath = "output_audio.wav"
        self.wavpath1 = "output_audio1.wav"
        try:
            self.change_sample_rate(self.wavpath, self.wavpath1)
        except Exception as e:
            print(f"Failed to change sample rate: {e}")
            self._run_flag = False
        
        try:
            self.rate, self.wav = wavfile.read(self.wavpath1, mmap=False)
            print(f"Loaded WAV file with rate: {self.rate}")
        except Exception as e:
            print(f"Failed to read WAV file: {e}")
            self.wav = None
            self._run_flag = False
        
        if self.wav is not None:
            # 归一化音频数据
            if self.wav.dtype == np.int16:
                self.wav = self.wav.astype(np.float32) / 32768.0
            elif self.wav.dtype == np.int32:
                self.wav = self.wav.astype(np.float32) / 2147483648.0
            elif self.wav.dtype == np.uint8:
                self.wav = (self.wav.astype(np.float32) - 128) / 128.0
            else:
                print(f"Unsupported WAV format: {self.wav.dtype}")
                self._run_flag = False
            
            # 确保采样率为16000
            self.sample_rate = 16000
            if self.rate != self.sample_rate:
                new_length = int(len(self.wav) * self.sample_rate / self.rate)
                if new_length > 0:
                    self.wav = resample(self.wav, new_length)
                    print(f"Resampled WAV to {self.sample_rate} Hz.")
                else:
                    print(f"Invalid resampling length: {new_length}")
                    self._run_flag = False
        else:
            print("WAV data is not loaded properly.")
            self._run_flag = False
        
        if self._run_flag:
            # 设置帧率和每帧样本数
            self.frame_rate = 25
            self.frame_duration = 1.0 / self.frame_rate
            self.samples_per_frame = int(self.sample_rate * self.frame_duration)
            self.index_ = 0
            
            # 初始化音频缓冲区
            self.audio_queue = queue.Queue(maxsize=20)  # 根据需要调整大小

            # 启动 FFmpeg 推流进程
            self.ffmpeg_process = self.start_ffmpeg()

    def change_sample_rate(self, input_file, output_file, target_sample_rate=16000):
        if not os.path.isfile(input_file):
            raise FileNotFoundError(f"Input file {input_file} not found.")
        command = ['ffmpeg', '-y', '-i', input_file, '-ar', str(target_sample_rate), output_file]
        try:
            result = subprocess.run(command, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            print(f"Sample rate changed to {target_sample_rate} Hz and saved as {output_file}.")
        except subprocess.CalledProcessError as e:
            error_msg = e.stderr.decode()
            print(f"Error occurred during sample rate change: {error_msg}")
            raise

    def start_ffmpeg(self):
        # FFmpeg 命令，用于接收视频和音频数据并推送到 RTMP 服务器
        rtmp_url = "rtmp://your_rtmp_server/live/stream"  # 替换为您的 RTMP 服务器地址

        ffmpeg_command = [
            'ffmpeg',
            '-y',
            # 视频输入配置
            '-f', 'rawvideo',
            '-pix_fmt', 'rgb24',
            '-s', '800x600',
            '-r', str(self.frame_rate),
            '-i', '-',  # 从标准输入接收视频
            # 音频输入配置
            '-f', 's16le',
            '-ar', str(self.sample_rate),
            '-ac', '1',
            '-i', '-',  # 从标准输入接收音频
            # 编码和输出配置
            '-c:v', 'libx264',
            '-pix_fmt', 'yuv420p',
            '-preset', 'veryfast',
            '-f', 'flv',
            rtmp_url
        ]

        # 启动 FFmpeg 进程，并设置 stdin 为管道
        ffmpeg_proc = subprocess.Popen(ffmpeg_command, stdin=subprocess.PIPE, stderr=subprocess.PIPE)

        # 启动一个线程来读取 FFmpeg 的 stderr，以便调试
        threading.Thread(target=self.read_ffmpeg_stderr, args=(ffmpeg_proc,), daemon=True).start()

        print(f"Started FFmpeg process for RTMP streaming to {rtmp_url}.")
        return ffmpeg_proc

    def read_ffmpeg_stderr(self, ffmpeg_proc):
        # 读取 FFmpeg 的 stderr 输出，用于调试
        while True:
            line = ffmpeg_proc.stderr.readline()
            if not line:
                break
            print(line.decode().strip())

    def run(self):
        if not self._run_flag:
            print("Initialization failed or models not loaded. Exiting thread.")
            return
        
        try:
            # 定义音频回调函数
            def audio_callback(outdata, frames, time_info, status):
                if status:
                    print(f"Stream status: {status}")
                try:
                    # 从队列中取出音频数据
                    data = self.audio_queue.get_nowait()
                except queue.Empty:
                    # 如果队列为空，则填充静音
                    data = np.zeros(frames, dtype=np.float32)
                    print("Audio queue empty, filling with silence.")
                # 将数据写入输出缓冲区
                outdata[:, 0] = data

                # 将音频数据发送到 FFmpeg
                if self.ffmpeg_process and self.ffmpeg_process.stdin:
                    try:
                        # 转换为16位小端格式
                        audio_int16 = np.int16(data * 32767)
                        self.ffmpeg_process.stdin.write(audio_int16.tobytes())
                        self.ffmpeg_process.stdin.flush()
                        print(f"Sent audio chunk at index {self.index_}")
                    except Exception as e:
                        print(f"Error sending audio to FFmpeg at index {self.index_}: {e}")

            # 开启音频输出流
            with sd.OutputStream(samplerate=self.sample_rate, channels=1, dtype='float32',
                                 callback=audio_callback, blocksize=self.samples_per_frame):
                print("Audio stream started.")
                
                # 记录开始时间
                start_time = time.perf_counter()
                
                while self._run_flag and self.index_ * self.samples_per_frame < len(self.wav):
                    # 计算目标时间
                    target_time = start_time + self.index_ * self.frame_duration
                    current_time = time.perf_counter()
                    remaining_time = target_time - current_time
                    
                    if remaining_time > 0:
                        time.sleep(remaining_time)
                    elif abs(remaining_time) > self.frame_duration:
                        print(f"Lagging by {-remaining_time:.2f} seconds at index {self.index_}")
                    
                    frame_start_time = time.perf_counter()  # 记录帧开始处理的时间
                    
                    start = self.index_ * self.samples_per_frame
                    end = start + self.samples_per_frame
                    if end > len(self.wav):
                        end = len(self.wav)
                    pcm_data = self.wav[start:end]
                    
                    # 处理音频块
                    try:
                        mouth_frame = self.audioModel.interface_frame(pcm_data)
                        print(f"Processed audio chunk at index {self.index_}")
                    except Exception as e:
                        print(f"Error processing audio at index {self.index_}: {e}")
                        mouth_frame = None
                    
                    # 渲染视频帧
                    try:
                        frame = self.renderModel.interface(mouth_frame)
                        if frame is not None:
                            print(f"Generated frame at index {self.index_}")
                        else:
                            print(f"RenderModel returned None at index {self.index_}")
                    except Exception as e:
                        print(f"Error generating frame at index {self.index_}: {e}")
                        frame = None
                    
                    # 发射信号到主线程更新视频帧
                    self.frame_signal.emit(frame)
                    print(f"Emitted frame at index {self.index_}")
                    
                    # 将音频数据推入队列
                    try:
                        self.audio_queue.put_nowait(pcm_data)
                        print(f"Enqueued audio chunk at index {self.index_}")
                    except queue.Full:
                        print("Audio queue is full, dropping audio chunk.")
                    
                    # 将视频帧数据发送到 FFmpeg
                    if frame is not None:
                        # 确保帧为 RGB 格式，800x600
                        frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) if frame.shape[2] == 3 else frame
                        frame_rgb = cv2.resize(frame_rgb, (800, 600))
                        # 将帧数据转换为字节，并发送给 FFmpeg
                        try:
                            self.ffmpeg_process.stdin.write(frame_rgb.tobytes())
                            self.ffmpeg_process.stdin.flush()
                            print(f"Sent video frame at index {self.index_}")
                        except Exception as e:
                            print(f"Error sending frame to FFmpeg at index {self.index_}: {e}")
                    
                    # 更新索引
                    self.index_ += 1
                    
                    # 计算处理时间并调整睡眠时间
                    frame_end_time = time.perf_counter()
                    processing_time = frame_end_time - frame_start_time
                    sleep_time = self.frame_duration - processing_time
                    if sleep_time > 0:
                        time.sleep(sleep_time)
                    else:
                        print(f"Warning: Processing time {processing_time:.4f}s exceeds frame duration at index {self.index_ - 1}")
                
                print("Audio stream finished.")

            # 在循环结束后，关闭 FFmpeg 的 stdin，以通知 FFmpeg 推流结束
            if self.ffmpeg_process and self.ffmpeg_process.stdin:
                self.ffmpeg_process.stdin.close()
            # 等待 FFmpeg 进程结束
            if self.ffmpeg_process:
                self.ffmpeg_process.wait()
                print("FFmpeg process terminated.")
        
        except Exception as e:
            print(f"Error in worker thread: {e}")
        print("Worker thread exiting.")
    
    def stop(self):
        print("Stopping worker thread...")
        self._run_flag = False
        self.wait()
        print("Worker thread stopped.")

def main():
    app = QtWidgets.QApplication(sys.argv)
    
    # 创建并显示主窗口
    window = VideoWindow()
    window.show()
    
    # 创建并启动后台线程
    worker = VideoAudioWorker()
    worker.frame_signal.connect(window.update_frame)
    worker.start()
    
    # 确保关闭窗口时停止线程
    def on_close():
        print("Closing application...")
        worker.stop()
        app.quit()
    
    window.destroyed.connect(on_close)
    
    # 运行应用程序
    sys.exit(app.exec())

if __name__ == "__main__":
    main()   该程序Enqueued audio chunk at index 58
Error sending frame to FFmpeg at index 58: [Errno 32] Broken pipe
Lagging by 0.12 seconds at index 59
Processed audio chunk at index 59
Error sending audio to FFmpeg at index 59: [Errno 32] Broken pipe
Generated frame at index 59
Emitted frame at index 59
Enqueued audio chunk at index 59
Error sending frame to FFmpeg at index 59: [Errno 32] Broken pipe
Lagging by 0.12 seconds at index 60
Processed audio chunk at index 60
Error sending audio to FFmpeg at index 60: [Errno 32] Broken pipe
Generated frame at index 60
Emitted frame at index 60
Enqueued audio chunk at index 60
Error sending frame to FFmpeg at index 60: [Errno 32] Broken pipe
Lagging by 0.12 seconds at index 61
Processed audio chunk at index 61
Error sending audio to FFmpeg at index 61: [Errno 32] Broken pipe
^CGenerated frame at index 61
Emitted frame at index 61
Enqueued audio chunk at index 61