Dana
7 天以前 33b4919c10b89a88141a1f508b4db5a9183e07e7
app/src/main/java/com/safeluck/floatwindow/manager/UsbCameraPushManager.java
@@ -3,12 +3,19 @@
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.WindowManager;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import com.alivc.live.pusher.AlivcAudioAACProfileEnum;
import timber.log.Timber;
@@ -27,65 +34,67 @@
import com.alivc.live.pusher.AlivcQualityModeEnum;
import com.alivc.live.pusher.AlivcResolutionEnum;
import com.anyun.libusbcamera.UsbCamera;
import com.anyun.libusbcamera.WatermarkParam;
import com.safeluck.floatwindow.MediaArgu;
import com.safeluck.floatwindow.ResponseVO;
import com.safeluck.floatwindow.util.AudioRecordManager;
import com.safeluck.floatwindow.util.GlobalData;
/**
 * USB摄像头推流管理器
 */
public class UsbCameraPushManager {
    private static final String TAG = "UsbCameraPushManager";
    private Context context;
    private MediaArgu mediaArgu;
    private PushCallback callback;
    // 阿里推流相关
    private AlivcLivePusher alivcPusher;
    private AlivcLivePushConfig alivcLivePushConfig;
    private SurfaceView previewSurfaceView;
    // USB摄像头相关
    private UsbCamera usbCamera;
    private PushThread pushThread;
    private boolean isRunning = false;
    private boolean cameraExists = false;
    private volatile boolean pushStarted = false;
    // 推流URL
    private String pushUrl;
    // 分辨率数组 [width, height]
    private int[] resolutionArr = new int[]{640, 480};
    // 是否开启摄像头加密
    private boolean ay_encrypt = false;
    // 预览 SurfaceView 和隐藏的 Window
    private WindowManager windowManager;
    // 音频推流线程池(单线程)
    private ExecutorService audioPushExecutor;
    /**
     * 推流回调接口
     */
    public interface PushCallback {
        void onResult(ResponseVO response);
    }
    public UsbCameraPushManager(Context context) {
        this.context = context;
    }
    /**
     * 设置回调
     */
    public void setCallback(PushCallback callback) {
        this.callback = callback;
    }
    /**
     * 开始推流
     */
@@ -94,45 +103,46 @@
            notifyCallback(1, -1, "MediaArgu is null");
            return;
        }
        this.mediaArgu = media;
        this.pushUrl = media.getUrl();
        if (pushUrl == null || pushUrl.isEmpty()) {
            notifyCallback(1, -2, "Push URL is empty");
            return;
        }
        // 设置分辨率
        if (media.getM_screen() != null) {
            resolutionArr[0] = media.getM_screen().getWidth();
            resolutionArr[1] = media.getM_screen().getHeight();
            Timber.d("设置分辨率: %dx%d", resolutionArr[0], resolutionArr[1]);
        }
        try {
            // 初始化推流SDK
            initAlivcPusher();
            setWaterMask();
            pushStarted = false;
            // 检查并打开USB摄像头
            if (!openUsbCamera()) {
                cameraExists = false;
                notifyCallback(1, -1, "USB摄像头打开失败");
                return;
            }
            cameraExists = true;
            Timber.d("USB摄像头打开成功");
            notifyCallback(1, 0, "推流线程已启动,等待推流状态就绪");
        } catch (Exception e) {
            Timber.e(e, "Failed to start push");
            notifyCallback(1, -3, "启动推流失败: " + e.getMessage());
        }
    }
    /**
     * 停止推流
     */
@@ -147,54 +157,53 @@
        pushStarted = false;
        notifyCallback(1, 4, "推流已停止");
    }
    /**
     * 初始化阿里推流
     */
    private void initAlivcPusher() {
        try {
            alivcLivePushConfig = new AlivcLivePushConfig();
            // 根据分辨率设置
            setResolutionFromArray(resolutionArr);
            // 建议用户使用20fps
            alivcLivePushConfig.setFps(AlivcFpsEnum.FPS_20);
            // 打开码率自适应
            alivcLivePushConfig.setEnableBitrateControl(true);
            // 设置横屏方向
            alivcLivePushConfig.setPreviewOrientation(AlivcPreviewOrientationEnum.ORIENTATION_LANDSCAPE_HOME_LEFT);
            // 设置音频编码模式
            alivcLivePushConfig.setAudioProfile(AlivcAudioAACProfileEnum.AAC_LC);
            // 设置摄像头类型
            alivcLivePushConfig.setCameraType(AlivcLivePushCameraTypeEnum.CAMERA_TYPE_BACK);
            // 设置视频编码模式为硬编码
            alivcLivePushConfig.setVideoEncodeMode(AlivcEncodeModeEnum.Encode_MODE_HARD);
            // 关闭美颜
            alivcLivePushConfig.setBeautyOn(false);
            // 清晰度优先模式
            alivcLivePushConfig.setQualityMode(AlivcQualityModeEnum.QM_RESOLUTION_FIRST);
            // 设置自定义流模式
            alivcLivePushConfig.setExternMainStream(true);
            alivcLivePushConfig.setAlivcExternMainImageFormat(AlivcImageFormat.IMAGE_FORMAT_YUV420P);
            // 初始化推流器
            alivcPusher = new AlivcLivePusher();
            alivcPusher.init(context.getApplicationContext(), alivcLivePushConfig);
            // 外部自定义流模式下,同样需要先开启预览,让状态从 INIT 进入 PREVIEWED
            // 创建一个隐藏的 Window 来承载 SurfaceView,确保 Surface 能够被创建
            windowManager = (WindowManager) context.getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
            previewSurfaceView = new SurfaceView(context.getApplicationContext());
            // 在 SurfaceView 的 surfaceCreated 回调中再启动预览,确保 Surface 已经创建
            previewSurfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
                @Override
@@ -225,7 +234,7 @@
                    Timber.d("previewSurfaceView surfaceDestroyed");
                }
            });
            // 将 SurfaceView 添加到隐藏的 Window 中,这样 Surface 才会被创建
            WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                    1, 1, // 1x1 像素,几乎不可见
@@ -239,7 +248,7 @@
            params.x = -1000; // 移到屏幕外
            params.y = -1000;
            params.alpha = 0.0f; // 完全透明
            try {
                windowManager.addView(previewSurfaceView, params);
                Timber.d("previewSurfaceView added to window");
@@ -257,12 +266,90 @@
            // 设置监听器
            setupListeners();
            Timber.d("AlivcPusher initialized successfully");
        } catch (Exception e) {
            Timber.e(e, "Failed to initialize AlivcPusher");
            notifyCallback(1, -3, "初始化推流SDK失败: " + e.getMessage());
        }
    }
    WatermarkParam watermarkParam;
    ArrayList<WatermarkParam> watermarkParamList = new ArrayList<>();
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    int baseY = 20;
    int fontSize= 24;
    private ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
    private void setWaterMask() {
        scheduledExecutorService.scheduleWithFixedDelay(() -> {
            if (pushStarted){
                if (!TextUtils.isEmpty(GlobalData.getInstance().getWaterMaskInfo())){
                    Log.i(TAG,"tieshuiin");
                    if (resolutionArr[0]==320&&resolutionArr[1]==240){
                        fontSize = 24;
                        baseY = 2;
                    }else if (resolutionArr[0]==640&&resolutionArr[1]==480){
                        fontSize = 32;
                        baseY = 4;
                    }else if (resolutionArr[0]==1280&&resolutionArr[1]==720){
                        fontSize = 48;
                        baseY = 6;
                    }else{
                        baseY = 2;
                        fontSize = 24;
                    }
                    String school = GlobalData.getInstance().parseWaterMaskInfo("school", "无", GlobalData.ShareType.STRING);
                    watermarkParam = new WatermarkParam(10,baseY,school);
                    watermarkParamList.clear();
                    watermarkParamList.add(watermarkParam);
                    String teacher = GlobalData.getInstance().parseWaterMaskInfo("teacher", "无", GlobalData.ShareType.STRING);
                    String stu = GlobalData.getInstance().parseWaterMaskInfo("student", "无", GlobalData.ShareType.STRING);
                    baseY = fontSize*11/10+baseY;
                    watermarkParam = new WatermarkParam(10,baseY,"教练:"+teacher+" 学员:"+stu);
                    watermarkParamList.add(watermarkParam);
                    double speed = GlobalData.getInstance().parseWaterMaskInfo("speed", 0.0, GlobalData.ShareType.DOUBLE);
                    String czh = GlobalData.getInstance().parseWaterMaskInfo("car_license", "无", GlobalData.ShareType.STRING) + GlobalData.getInstance().getCameraTag;
                    baseY = fontSize*11/10+baseY;
                    watermarkParam = new WatermarkParam(10,resolutionArr[1]-baseY,czh +"    "+String.format("速度:%.1f",speed));
                    watermarkParamList.add(watermarkParam);
                    double latitude = GlobalData.getInstance().parseWaterMaskInfo("latitude", 29.51228918, GlobalData.ShareType.DOUBLE);
                    double longitude = GlobalData.getInstance().parseWaterMaskInfo("longitude", 106.45556208, GlobalData.ShareType.DOUBLE);
//                            new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())
                    baseY = fontSize*11/10+baseY;
                    watermarkParam = new WatermarkParam(10,resolutionArr[1]-fontSize,  String.format("%.6f %.6f", latitude, longitude)+" "+sdf.format(new Date()));
                    watermarkParamList.add(watermarkParam);
                    if (resolutionArr[0]==320&&resolutionArr[1]==240){
                        usbCamera.enableWatermark(true,"/system/ms_unicode_24.bin");
                        usbCamera.setWatermark(3,fontSize,1,watermarkParamList);
                    }else if (resolutionArr[0]==640&&resolutionArr[1]==480){
                        usbCamera.enableWatermark(true,"/system/ms_unicode_32.bin");
                        usbCamera.setWatermark(3,fontSize,1,watermarkParamList);
                    }else if (resolutionArr[0]==1280&&resolutionArr[1]==720){
                        usbCamera.enableWatermark(true,"/system/ms_unicode_48.bin");
                        usbCamera.setWatermark(3,fontSize,1,watermarkParamList);
                    }else{
                        usbCamera.enableWatermark(true,"/system/ms_unicode_24.bin");
                        usbCamera.setWatermark(3,fontSize,1,watermarkParamList);
                    }
                }
            }
        },1,1, TimeUnit.SECONDS);
    }
    private Handler mainHandler = new Handler(Looper.getMainLooper());
@@ -291,12 +378,12 @@
                },1000);
            }
            @Override
            public void onPreviewStoped(AlivcLivePusher alivcLivePusher) {
                Timber.d("onPreviewStoped");
            }
            @Override
            public void onPushStarted(AlivcLivePusher alivcLivePusher) {
                Timber.d("onPushStarted");
@@ -304,50 +391,50 @@
//                startAudioTransfer();
                notifyCallback(1, 0, "推流已开始,分辨率: " + resolutionArr[0] + "x" + resolutionArr[1]);
            }
            @Override
            public void onPushPauesed(AlivcLivePusher alivcLivePusher) {
                Timber.d("onPushPauesed");
            }
            @Override
            public void onPushResumed(AlivcLivePusher alivcLivePusher) {
                Timber.d("onPushResumed");
            }
            @Override
            public void onPushStoped(AlivcLivePusher alivcLivePusher) {
                Timber.d("onPushStoped");
                pushStarted = false;
                notifyCallback(1, 4, "推流已停止");
            }
            @Override
            public void onPushRestarted(AlivcLivePusher alivcLivePusher) {
                Timber.d("onPushRestarted");
            }
            @Override
            public void onFirstFramePreviewed(AlivcLivePusher alivcLivePusher) {
                Timber.d("onFirstFramePreviewed");
            }
            @Override
            public void onDropFrame(AlivcLivePusher alivcLivePusher, int i, int i1) {
                // 丢帧回调
            }
            @Override
            public void onAdjustBitRate(AlivcLivePusher alivcLivePusher, int i, int i1) {
                // 码率调整回调
            }
            @Override
            public void onAdjustFps(AlivcLivePusher alivcLivePusher, int i, int i1) {
                // 帧率调整回调
            }
        });
        // 错误监听器
        alivcPusher.setLivePushErrorListener(new AlivcLivePushErrorListener() {
            @Override
@@ -358,7 +445,7 @@
                    alivcLivePusher.stopPush();
                }
            }
            @Override
            public void onSDKError(AlivcLivePusher alivcLivePusher, AlivcLivePushError alivcLivePushError) {
                Timber.e("onSDKError: %s", alivcLivePushError.toString());
@@ -368,7 +455,7 @@
                }
            }
        });
        // 网络监听器
        alivcPusher.setLivePushNetworkListener(new AlivcLivePushNetworkListener() {
            @Override
@@ -376,54 +463,54 @@
                Timber.w("onNetworkPoor");
                notifyCallback(1, 3, "网络较差");
            }
            @Override
            public void onNetworkRecovery(AlivcLivePusher alivcLivePusher) {
                Timber.d("onNetworkRecovery");
                notifyCallback(1, 0, "网络恢复");
            }
            @Override
            public void onReconnectStart(AlivcLivePusher alivcLivePusher) {
                Timber.d("onReconnectStart");
            }
            @Override
            public void onReconnectFail(AlivcLivePusher alivcLivePusher) {
                Timber.e("onReconnectFail");
                notifyCallback(1, 2, "重连失败");
            }
            @Override
            public void onReconnectSucceed(AlivcLivePusher alivcLivePusher) {
                Timber.d("onReconnectSucceed");
                notifyCallback(1, 0, "重连成功");
            }
            @Override
            public void onSendDataTimeout(AlivcLivePusher alivcLivePusher) {
                Timber.w("onSendDataTimeout");
            }
            @Override
            public void onConnectFail(AlivcLivePusher alivcLivePusher) {
                Timber.e("onConnectFail");
                notifyCallback(1, -2, "连接失败");
            }
            @Override
            public String onPushURLAuthenticationOverdue(AlivcLivePusher alivcLivePusher) {
                Timber.w("onPushURLAuthenticationOverdue");
                return null;
            }
            @Override
            public void onSendMessage(AlivcLivePusher alivcLivePusher) {
                // 发送消息回调
            }
        });
    }
    /**
     * 根据分辨率数组设置分辨率
     */
@@ -454,7 +541,7 @@
            }
        }
    }
    /**
     * 打开USB摄像头
     */
@@ -490,7 +577,7 @@
            return false;
        }
    }
    /**
     * 启动推流线程
     */
@@ -502,7 +589,7 @@
            Timber.d("Push thread started");
        }
    }
    /**
     * 停止推流线程
     */
@@ -518,7 +605,7 @@
        }
        Timber.d("Push thread stopped");
    }
    /**
     * 释放阿里推流资源
     */
@@ -533,13 +620,13 @@
            }
            previewSurfaceView = null;
        }
        if (alivcPusher != null) {
            try {
                AlivcLivePushStats stats = alivcPusher.getCurrentStatus();
                Timber.d("当前推流状态: %s", stats != null ? stats.name() : "null");
                if (stats != null && (stats == AlivcLivePushStats.PUSHED ||
                if (stats != null && (stats == AlivcLivePushStats.PUSHED ||
                    stats == AlivcLivePushStats.PREVIEWED)) {
                    alivcPusher.stopPush();
                }
@@ -552,7 +639,7 @@
        alivcLivePushConfig = null;
        pushStarted = false;
    }
    /**
     * 推流线程
     */
@@ -561,18 +648,18 @@
        public void run() {
            super.run();
            Timber.d("PushThread started");
            try {
                int width = resolutionArr[0];
                int height = resolutionArr[1];
                final long startTimeNs = System.nanoTime();
                // 计算YUV420缓冲区大小
                int bufferSize = width * height * 3 / 2;
                byte[] buffer = new byte[bufferSize];
                Timber.d("开始推送视频数据,分辨率: %dx%d", width, height);
                // 循环处理摄像头数据
                while (isRunning && cameraExists) {
                    // 处理摄像头数据