package com.ttstd.controlled.webrtc;

import android.content.Context;
import android.util.Log;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.ttstd.control.Action;
import com.ttstd.control.ControlMessage;
import com.ttstd.controlled.signaling.SignalMessage;
import com.ttstd.controlled.signaling.WebSocketClient;

import org.webrtc.DataChannel;
import org.webrtc.DefaultVideoDecoderFactory;
import org.webrtc.DefaultVideoEncoderFactory;
import org.webrtc.EglBase;
import org.webrtc.IceCandidate;
import org.webrtc.MediaConstraints;
import org.webrtc.MediaStream;
import org.webrtc.PeerConnection;
import org.webrtc.PeerConnectionFactory;
import org.webrtc.SessionDescription;
import org.webrtc.SurfaceTextureHelper;
import org.webrtc.VideoCapturer;
import org.webrtc.VideoCodecInfo;
import org.webrtc.VideoSink;
import org.webrtc.VideoSource;
import org.webrtc.VideoTrack;

import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class WebRtcClient {

    private static final String TAG = "WebRtcClient";
    private static final String VIDEO_TRACK_ID = "screen_track";
    private static final String STREAM_ID = "screen_stream";
    private static final String DATA_CHANNEL_LABEL = "control_channel";
    private static final String VIDEO_CHANNEL_LABEL = "video_channel"; // 自编码视频透传通道（控制端创建）

    // 码率优化参数（单位 kbps）。
    private static final int MIN_BITRATE_KBPS = 1000;   // 1 Mbps
    private static final int START_BITRATE_KBPS = 2000; // 2 Mbps
    private static final int MAX_BITRATE_KBPS = 8000;   // 8 Mbps

    private final Context context;
    private final WebSocketClient wsClient;
    private final String deviceId;
    private final Gson gson = new GsonBuilder().disableHtmlEscaping().create();

    private PeerConnectionFactory peerConnectionFactory;
    private PeerConnection peerConnection;
    private DataChannel dataChannel;
    private DataChannel videoDataChannel; // 自编码视频透传（控制端创建的 video_channel）
    private VideoSource videoSource;
    private VideoTrack videoTrack;
    private org.webrtc.RtpSender videoSender;
    private EglBase eglBase;
    private String currentControllerId;

    private VideoCapturer videoCapturer;
    private int videoWidth;
    private int videoHeight;
    private int videoFps;

    private InputCommandCallback inputCallback;

    /** 自编码模式帧监听器：WebRTC 采集到的每一帧都会转发给自编码编码器（共享同一条屏幕采集）。 */
    public interface SelfCodecFrameListener {
        void onFrame(org.webrtc.VideoFrame frame);
    }

    private SelfCodecFrameListener selfCodecFrameListener;

    private RemoteDisconnectCallback remoteDisconnectCallback;
    private boolean remoteDisconnectNotified = false;

    public interface IceEventListener {
        void onIceConnected();
    }

    private IceEventListener iceEventListener;

    // 关键修复：控制端（网页）会在被控端弹出"接受"对话框期间就把 ICE 候选发过来，
    // 此时 peerConnection 尚未创建 / 远端描述(offer)尚未设置，直接 addIceCandidate 会被丢弃。
    // 因此先把候选缓存起来，等 setRemoteDescription(offer) 完成后再统一 flush。
    private boolean remoteDescriptionSet = false;
    private final List<IceCandidate> pendingRemoteCandidates = new ArrayList<>();

    public interface InputCommandCallback {
        void onCommandReceived(byte[] data);
    }

    /** 远程控制端（PeerConnection / DataChannel）断开时的回调。 */
    public interface RemoteDisconnectCallback {
        void onRemoteDisconnected(String controllerId);
    }

    public WebRtcClient(Context context, WebSocketClient wsClient, String deviceId) {
        this.context = context;
        this.wsClient = wsClient;
        this.deviceId = deviceId;
    }

    public void setInputCallback(InputCommandCallback callback) {
        this.inputCallback = callback;
    }

    public void setRemoteDisconnectCallback(RemoteDisconnectCallback callback) {
        this.remoteDisconnectCallback = callback;
    }

    public void setIceEventListener(IceEventListener listener) {
        this.iceEventListener = listener;
    }

    private void notifyRemoteDisconnected() {
        if (remoteDisconnectNotified) return;
        remoteDisconnectNotified = true;
        if (remoteDisconnectCallback != null) {
            remoteDisconnectCallback.onRemoteDisconnected(currentControllerId);
        }
    }

    public void initialize(EglBase eglBase) {
        this.eglBase = eglBase;
        PeerConnectionFactory.InitializationOptions initOptions =
                PeerConnectionFactory.InitializationOptions.builder(context)
                        .setFieldTrials("WebRTC-H264HighProfile/Enabled/WebRTC-Video-HwAcceleration/Enabled/WebRTC-FlexFEC-03/Enabled/")
                        .createInitializationOptions();
        PeerConnectionFactory.initialize(initOptions);

        // 第三个参数 enableH264HighProfile 设为 false：H264 仅作兜底，
        // 使用 Constrained Baseline（profile-level-id 42e01f），各浏览器/桌面端均可稳定解码；
        // 主用 VP8/VP9，由 optimizeSdp 在 m=video 行优先排列。
        DefaultVideoEncoderFactory encoderFactory = new DefaultVideoEncoderFactory(eglBase.getEglBaseContext(), true, false);
        DefaultVideoDecoderFactory decoderFactory = new DefaultVideoDecoderFactory(eglBase.getEglBaseContext());

        // 打印所有支持的编码格式
        Log.i(TAG, "Supported Encoders:");
        for (VideoCodecInfo info : encoderFactory.getSupportedCodecs()) {
            Log.i(TAG, "  - Codec: " + info.name + ", params: " + info.params);
        }

        PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
        peerConnectionFactory = PeerConnectionFactory.builder()
                .setOptions(options)
                .setVideoEncoderFactory(encoderFactory)
                .setVideoDecoderFactory(decoderFactory)
                .createPeerConnectionFactory();
    }

    public void setVideoCapturer(VideoCapturer capturer, int width, int height, int fps) {
        this.videoCapturer = capturer;
        this.videoWidth = width;
        this.videoHeight = height;
        this.videoFps = fps;

        SurfaceTextureHelper surfaceTextureHelper = SurfaceTextureHelper.create("CaptureThread", eglBase.getEglBaseContext());
        // 对于屏幕共享，isScreencast 设为 true，这会优化细节保留
        videoSource = peerConnectionFactory.createVideoSource(true);
        capturer.initialize(surfaceTextureHelper, context, videoSource.getCapturerObserver());
        capturer.startCapture(width, height, fps);

        videoTrack = peerConnectionFactory.createVideoTrack(VIDEO_TRACK_ID, videoSource);
        if (localSink != null) {
            videoTrack.addSink(localSink);
        }
        // 把视频轨道的每一帧再转发给自编码帧监听器（如果已设置）。
        // 只在此处添加一次（videoTrack 仅创建一次），避免重复连接时重复挂 sink 导致重复编码。
        // 自编码模式复用同一条屏幕采集管线，无需再申请第二个 VirtualDisplay，
        // 兼容 Android 14 单次投屏授权令牌的限制。
        videoTrack.addSink(new VideoSink() {
            @Override
            public void onFrame(org.webrtc.VideoFrame frame) {
                if (selfCodecFrameListener != null) {
                    selfCodecFrameListener.onFrame(frame);
                }
            }
        });
    }

    /**
     * 运行时切换屏幕采集分辨率（无需重建采集器/连接）。
     * 通过 {@link org.webrtc.VideoCapturer#changeCaptureFormat} 平滑改变编码尺寸，
     * 并触发一次关键帧，避免远端解码器出现花屏/卡顿。
     *
     * @param width  目标宽度（像素，需为偶数）
     * @param height 目标高度（像素，需为偶数）
     * @param fps    目标帧率，<=0 时沿用当前帧率
     */
    public void changeCaptureFormat(int width, int height, int fps) {
        if (videoCapturer == null) {
            Log.w(TAG, "changeCaptureFormat ignored: capturer not ready");
            return;
        }
        int targetFps = fps > 0 ? fps : this.videoFps;
        if (targetFps <= 0) targetFps = 30;
        Log.i(TAG, "Changing capture format -> " + width + "x" + height + " @ " + targetFps + "fps");
        try {
            videoCapturer.changeCaptureFormat(width, height, targetFps);
        } catch (Exception e) {
            Log.e(TAG, "changeCaptureFormat failed", e);
            return;
        }
        this.videoWidth = width;
        this.videoHeight = height;
        this.videoFps = targetFps;
        // 编码尺寸变化后诱发关键帧，使远端立即刷新画面。
        triggerKeyFrame();
        // 主动把最新采集分辨率上报给控制端，使其 UI 与实际保持一致。
        sendResolutionReport(width, height, targetFps);
    }

    /**
     * 通过 DataChannel 把当前实际采集分辨率上报给控制端（action=REPORT_RESOLUTION）。
     * 控制端（网页/Android/Flutter）据此同步展示当前分辨率，避免 UI 与实际不一致。
     */
    /** 被控端支持的帧率档位（随 REPORT_RESOLUTION 上报给控制端）。 */
    private List<Integer> supportedFpsList;

    public void setSupportedFpsList(List<Integer> fpsList) {
        this.supportedFpsList = fpsList;
    }

    public void sendResolutionReport(int width, int height, int fps) {
        if (dataChannel == null || dataChannel.state() != DataChannel.State.OPEN) {
            Log.d(TAG, "sendResolutionReport skipped: dataChannel not open");
            return;
        }
        try {
            ControlMessage.Builder builder = ControlMessage.newBuilder()
                    .setAction(Action.REPORT_RESOLUTION)
                    .setWidth(width)
                    .setHeight(height)
                    .setFps(fps);
            if (supportedFpsList != null && !supportedFpsList.isEmpty()) {
                builder.addAllSupportedFps(supportedFpsList);
            }
            ControlMessage report = builder.build();
            ByteBuffer buffer = ByteBuffer.wrap(report.toByteArray());
            // binary=true：必须作为二进制发送，控制端按 protobuf 二进制解析；
            // 若传 false 会被当作文本，Web 端收到的将是字符串导致解码失败。
            dataChannel.send(new DataChannel.Buffer(buffer, true));
            Log.i(TAG, "Reported capture resolution -> " + width + "x" + height + " @ " + fps + "fps");
        } catch (Exception e) {
            Log.e(TAG, "sendResolutionReport failed", e);
        }
    }

    /** 返回当前采集分辨率（像素），用于 UI 展示。 */
    public int[] getCurrentResolution() {
        return new int[]{ videoWidth, videoHeight, videoFps };
    }

    private VideoSink localSink;

    public void setLocalSink(VideoSink sink) {
        this.localSink = sink;
        if (videoTrack != null && sink != null) {
            videoTrack.addSink(sink);
        }
    }

    public EglBase.Context getEglContext() {
        return eglBase != null ? eglBase.getEglBaseContext() : null;
    }

    public void createPeerConnectionAndAnswer(String offerSdp, String controllerId) {
        if (peerConnection != null) {
            Log.d(TAG, "Closing previous peer connection");
            closeCurrentConnection();
        }
        this.currentControllerId = controllerId;
        this.remoteDisconnectNotified = false;
        this.remoteDescriptionSet = false;
        this.pendingRemoteCandidates.clear();

        List<PeerConnection.IceServer> iceServers = new ArrayList<>();
        // 建议：如果你有 TURN 服务器，请务必在此处添加，例如：
        // iceServers.add(PeerConnection.IceServer.builder("turn:your_turn_server").setUsername("user").setPassword("pass").createIceServer());
        iceServers.add(PeerConnection.IceServer.builder("turn:175.178.213.60:3478").setUsername("fanhuitong").setPassword("Fan19961207..").createIceServer());
        iceServers.add(PeerConnection.IceServer.builder("stun:175.178.213.60:3478").createIceServer());
        iceServers.add(PeerConnection.IceServer.builder("turn:47.242.112.133:3478").setUsername("ttstd").setPassword("fanhuitong").createIceServer());
        iceServers.add(PeerConnection.IceServer.builder("stun:47.242.112.133:3478").createIceServer());
        // iceServers.add(PeerConnection.IceServer.builder("turn:192.168.5.224:3478").setUsername("tt").setPassword("fht").createIceServer());
        // iceServers.add(PeerConnection.IceServer.builder("stun:192.168.5.224:3478").createIceServer());
        iceServers.add(PeerConnection.IceServer.builder("stun:stun.l.google.com:19302").createIceServer());
        iceServers.add(PeerConnection.IceServer.builder("stun:stun1.l.google.com:19302").createIceServer());
        iceServers.add(PeerConnection.IceServer.builder("stun:stun2.l.google.com:19302").createIceServer());

        PeerConnection.RTCConfiguration config = new PeerConnection.RTCConfiguration(iceServers);
        config.sdpSemantics = PeerConnection.SdpSemantics.UNIFIED_PLAN;
        config.continualGatheringPolicy = PeerConnection.ContinualGatheringPolicy.GATHER_CONTINUALLY;
        config.iceCandidatePoolSize = 10;
        // 建议启用，以便在复杂网络下有更好的表现
        config.tcpCandidatePolicy = PeerConnection.TcpCandidatePolicy.ENABLED;

        PeerObserver peerObserver = new PeerObserver(new PeerObserver.PeerEventListener() {
            @Override
            public void onIceCandidate(IceCandidate candidate) {
                sendIceCandidate(candidate, controllerId);
            }

            @Override
            public void onIceConnectionChange(PeerConnection.IceConnectionState newState) {
                Log.d(TAG, "ICE connection state: " + newState);
                if (newState == PeerConnection.IceConnectionState.CONNECTED) {
                    // 连接成功时，诱发编码器产生一个关键帧。
                    // 外部 ScreenCaptureService 可通过 registerObserver 等方式监听，或者我们直接在此处回调。
                    triggerKeyFrame();
                    if (iceEventListener != null) iceEventListener.onIceConnected();
                } else if (newState == PeerConnection.IceConnectionState.DISCONNECTED
                        || newState == PeerConnection.IceConnectionState.FAILED
                        || newState == PeerConnection.IceConnectionState.CLOSED) {
                    // 远程控制端断开（网络中断 / 主动退出），通知上层提示用户。
                    notifyRemoteDisconnected();
                }
            }

            @Override
            public void onDataChannel(DataChannel dc) {
                Log.d(TAG, "Data channel received: " + dc.label());
                handleDataChannel(dc);
            }

            @Override
            public void onAddStream(MediaStream stream) {
            }

            @Override
            public void onRemoveStream(MediaStream stream) {
            }
        });

        peerConnection = peerConnectionFactory.createPeerConnection(config, peerObserver);

        if (videoTrack != null) {
            videoSender = peerConnection.addTrack(videoTrack, Collections.singletonList(STREAM_ID));
        }

        // 注意：DataChannel 由控制端（Offer 方）创建并协商，
        // 本端通过 PeerObserver.onDataChannel 回调接收（见下方注册），
        // 无需（也不应）再创建同名通道，否则会与控制端通道冲突。
        // 控制端创建时已使用 ordered=false + maxRetransmits=0（无序无重传），
        // 因此本端收到的所有指令均为最低延迟模式，无需额外配置。

        // 设置远端 SDP (Offer)
        SessionDescription remoteSdp = new SessionDescription(SessionDescription.Type.OFFER, offerSdp);
        peerConnection.setRemoteDescription(new AppSdpObserver(new AppSdpObserver.SdpEventListener() {
            @Override
            public void onCreateSuccess(SessionDescription sdp) {
            }

            @Override
            public void onSetSuccess() {
                // 远端描述(offer)已就绪，先 flush 之前缓存的控制端候选。
                remoteDescriptionSet = true;
                flushPendingCandidates();
                // 创建 Answer
                MediaConstraints constraints = new MediaConstraints();
                peerConnection.createAnswer(new AppSdpObserver(new AppSdpObserver.SdpEventListener() {
                    @Override
                    public void onCreateSuccess(SessionDescription sdp) {
                        // 【关键修复】在 setLocalDescription 之前修改 SDP
                        // 这样本地和远端都会统一使用优化后的 H264 配置
                        String modifiedSdpDescription = optimizeSdp(sdp.description);
                        SessionDescription modifiedSdp = new SessionDescription(sdp.type, modifiedSdpDescription);

                        peerConnection.setLocalDescription(new AppSdpObserver(new AppSdpObserver.SdpEventListener() {
                            @Override
                            public void onCreateSuccess(SessionDescription s) {
                            }

                            @Override
                            public void onSetSuccess() {
                                Log.d(TAG, "onSetSuccess: modifiedSdp = " + modifiedSdpDescription);
                                sendAnswer(modifiedSdpDescription, controllerId);
                            }

                            @Override
                            public void onCreateFailure(String error) {
                                Log.e(TAG, "setLocalDescription onCreateFailure: " + error);
                            }

                            @Override
                            public void onSetFailure(String error) {
                                Log.e(TAG, "setLocalDescription onSetFailure: " + error);
                            }
                        }), modifiedSdp);
                    }

                    @Override
                    public void onSetSuccess() {
                    }

                    @Override
                    public void onCreateFailure(String error) {
                        Log.e(TAG, "createAnswer onCreateFailure: " + error);
                    }

                    @Override
                    public void onSetFailure(String error) {
                    }
                }), constraints);
            }

            @Override
            public void onCreateFailure(String error) {
            }

            @Override
            public void onSetFailure(String error) {
                Log.e(TAG, "setRemoteDescription onSetFailure: " + error);
            }
        }), remoteSdp);
    }

    public void addIceCandidate(IceCandidate candidate) {
        if (peerConnection == null || !remoteDescriptionSet) {
            // 连接尚未就绪（还在等待用户接受 / 远端描述未设置），先缓存避免候选被丢弃。
            pendingRemoteCandidates.add(candidate);
            return;
        }
        if (peerConnection != null) {
            peerConnection.addIceCandidate(candidate);
        }
    }

    private void flushPendingCandidates() {
        if (peerConnection == null) return;
        for (IceCandidate c : pendingRemoteCandidates) {
            peerConnection.addIceCandidate(c);
        }
        pendingRemoteCandidates.clear();
    }

    public void sendInputResponse(String responseJson) {
        if (dataChannel != null && dataChannel.state() == DataChannel.State.OPEN) {
            DataChannel.Buffer buffer = new DataChannel.Buffer(
                    ByteBuffer.wrap(responseJson.getBytes(StandardCharsets.UTF_8)), false);
            dataChannel.send(buffer);
        }
    }

    public void close() {
        closeCurrentConnection();
        if (peerConnectionFactory != null) {
            peerConnectionFactory.dispose();
            peerConnectionFactory = null;
        }
    }

    private void closeCurrentConnection() {
        remoteDescriptionSet = false;
        pendingRemoteCandidates.clear();
        if (dataChannel != null) {
            try {
                dataChannel.unregisterObserver();
            } catch (Exception e) {
            }
            dataChannel.close();
            dataChannel.dispose();
            dataChannel = null;
        }
        if (videoDataChannel != null) {
            try {
                videoDataChannel.close();
            } catch (Exception e) {
            }
            try {
                videoDataChannel.dispose();
            } catch (Exception e) {
            }
            videoDataChannel = null;
        }
        if (peerConnection != null) {
            peerConnection.close();
            peerConnection.dispose();
            peerConnection = null;
        }
    }

    // ---- 屏幕串流"自编码"模式相关 ----

    /** 返回控制端建立的 video DataChannel（用于自编码视频透传），可能为空。 */
    public DataChannel getVideoDataChannel() {
        return videoDataChannel;
    }

    /** video DataChannel 是否已打开。 */
    public boolean isVideoDataChannelOpen() {
        return videoDataChannel != null && videoDataChannel.state() == DataChannel.State.OPEN;
    }

    /** 设置自编码帧监听器：为 null 时不再转发采集帧（WebRTC 模式）。 */
    public void setSelfCodecFrameListener(SelfCodecFrameListener listener) {
        this.selfCodecFrameListener = listener;
    }

    /** 暂停 WebRTC 默认屏幕采集（切到自编码模式时调用）。 */
    public void stopCapture() {
        if (videoCapturer != null) {
            try {
                videoCapturer.stopCapture();
            } catch (InterruptedException e) {
                Log.w(TAG, "stopCapture interrupted", e);
            }
        }
    }

    /** 恢复 WebRTC 默认屏幕采集（从自编码模式切回时调用）。 */
    public void startCapture() {
        if (videoCapturer != null) {
            try {
                videoCapturer.startCapture(videoWidth, videoHeight, videoFps);
            } catch (RuntimeException e) {
                Log.e(TAG, "startCapture failed", e);
            }
        }
    }

    /** 经 control DataChannel 发送控制类 protobuf 消息（如串流模式回报）。 */
    public void sendControlCommand(ControlMessage message) {
        if (dataChannel == null || dataChannel.state() != DataChannel.State.OPEN) {
            Log.d(TAG, "sendControlCommand skipped: dataChannel not open");
            return;
        }
        try {
            ByteBuffer buffer = ByteBuffer.wrap(message.toByteArray());
            dataChannel.send(new DataChannel.Buffer(buffer, true));
        } catch (Exception e) {
            Log.e(TAG, "sendControlCommand failed", e);
        }
    }

    private void handleDataChannel(DataChannel dc) {
        // 区分控制端建立的两条通道：video_channel 用于本端自编码视频透传（仅发送），
        // 其余（control_channel）用于指令收发。
        if (VIDEO_CHANNEL_LABEL.equals(dc.label())) {
            this.videoDataChannel = dc;
            Log.d(TAG, "Video data channel received: " + dc.label());
            return;
        }
        // 保存控制端建立的通道引用（替代原先本端自建的通道）。
        this.dataChannel = dc;
        dc.registerObserver(new DataChannel.Observer() {
            @Override
            public void onBufferedAmountChange(long previousAmount) {
            }

            @Override
            public void onStateChange() {
                Log.d(TAG, "DataChannel state: " + dc.state());
                if (dc.state() == DataChannel.State.OPEN) {
                    // 通道建立后，立即把当前实际采集分辨率上报给控制端（初始值或最近一次切换值）。
                    sendResolutionReport(videoWidth, videoHeight, videoFps);
                } else if (dc.state() == DataChannel.State.CLOSED) {
                    notifyRemoteDisconnected();
                }
            }

            @Override
            public void onMessage(DataChannel.Buffer buffer) {
                byte[] data = new byte[buffer.data.remaining()];
                buffer.data.get(data);
                Log.d(TAG, "DataChannel message received, bytes=" + data.length);
                if (inputCallback != null) {
                    inputCallback.onCommandReceived(data);
                }
            }
        });
    }

    private String optimizeSdp(String sdp) {
        String[] lines = sdp.split("\n");
        StringBuilder newSdp = new StringBuilder();

        // 按编解码器类型分组收集 payload type。
        // 重排时优先 VP8/VP9（控制端/浏览器原生解码兼容性最好，桌面端与 Chromium/Linux 均可稳定硬/软解），
        // H264 放最后作为兜底，避免部分环境无法解码 H264 High Profile 而黑屏。
        List<String> vp8 = new ArrayList<>();
        List<String> vp9 = new ArrayList<>();
        List<String> h264 = new ArrayList<>();
        for (String line : lines) {
            String trimmedLine = line.trim();
            if (trimmedLine.startsWith("a=rtpmap:")) {
                String payload = trimmedLine.split(":")[1].split(" ")[0];
                if (trimmedLine.contains("VP8/90000")) {
                    vp8.add(payload);
                } else if (trimmedLine.contains("VP9/90000")) {
                    vp9.add(payload);
                } else if (trimmedLine.contains("H264/90000")) {
                    h264.add(payload);
                }
            }
        }

        List<String> videoPayloads = new ArrayList<>();
        videoPayloads.addAll(vp8);
        videoPayloads.addAll(vp9);
        videoPayloads.addAll(h264);

        if (videoPayloads.isEmpty()) {
            return sdp;
        }

        for (String line : lines) {
            String trimmedLine = line.trim();
            if (trimmedLine.isEmpty()) continue;

            if (trimmedLine.startsWith("m=video")) {
                String[] parts = trimmedLine.split(" ");
                if (parts.length > 3) {
                    StringBuilder mLine = new StringBuilder(parts[0] + " " + parts[1] + " " + parts[2]);
                    for (String payload : videoPayloads) {
                        mLine.append(" ").append(payload);
                    }
                    for (int i = 3; i < parts.length; i++) {
                        if (!videoPayloads.contains(parts[i])) {
                            mLine.append(" ").append(parts[i]);
                        }
                    }
                    newSdp.append(mLine).append("\r\n");
                } else {
                    newSdp.append(trimmedLine).append("\r\n");
                }
            } else if (trimmedLine.startsWith("a=fmtp:")) {
                if (videoPayloads.contains(trimmedLine.split(":")[1].split(" ")[0])) {
                    String bonus = ";x-google-start-bitrate=" + START_BITRATE_KBPS
                            + ";x-google-max-bitrate=" + MAX_BITRATE_KBPS
                            + ";x-google-min-bitrate=" + MIN_BITRATE_KBPS;
                    newSdp.append(trimmedLine).append(bonus).append("\r\n");
                } else {
                    newSdp.append(trimmedLine).append("\r\n");
                }
            } else {
                newSdp.append(trimmedLine).append("\r\n");
            }
        }
        return newSdp.toString();
    }

    private void sendAnswer(String sdp, String controllerId) {
        SignalMessage msg = new SignalMessage();
        msg.setType("ANSWER");
        msg.setFromDeviceId(deviceId);
        msg.setToDeviceId(controllerId);
        msg.setDeviceType("CONTROLLED");

        JsonObject payload = new JsonObject();
        payload.addProperty("sdp", sdp);
        msg.setPayload(payload.toString());

        wsClient.sendMessage(msg);
    }

    private void sendIceCandidate(IceCandidate candidate, String controllerId) {
        SignalMessage msg = new SignalMessage();
        msg.setType("ICE_CANDIDATE");
        msg.setFromDeviceId(deviceId);
        msg.setToDeviceId(controllerId);
        msg.setDeviceType("CONTROLLED");

        JsonObject payload = new JsonObject();
        payload.addProperty("sdpMid", candidate.sdpMid);
        payload.addProperty("sdpMLineIndex", candidate.sdpMLineIndex);
        payload.addProperty("candidate", candidate.sdp);
        msg.setPayload(payload.toString());

        wsClient.sendMessage(msg);
    }

    /**
     * 通过重新应用 RtpParameters 诱发编码器生成关键帧（I-Frame）。
     * 相比重启采集器，这种方法更平滑，不会导致 MediaProjection 管道断开。
     */
    public void triggerKeyFrame() {
        if (videoSender != null) {
            Log.d(TAG, "Triggering key frame via RtpSender parameters update");
            org.webrtc.RtpParameters parameters = videoSender.getParameters();
            if (parameters != null && !parameters.encodings.isEmpty()) {
                // 即使不修改任何参数，重新设置一次 setParameters 也会诱发许多编码器产生 I-Frame
                videoSender.setParameters(parameters);
            }
        }
    }

    /** 即时请求自编码器产生一个关键帧。 */
    public void requestSelfCodecKeyFrame(SelfCodecEncoder encoder) {
        if (encoder != null) {
            Log.d(TAG, "Requesting key frame for self codec encoder");
            encoder.requestKeyFrame();
        }
    }
}
