1.SurfaceView 在 Service 中创建,但没有被添加到可见的 View hierarchy,因此 Surface 不会被创建,surfaceCreated 不会触发。
修复内容:
添加了 WindowManager:用于管理隐藏的 Window
将 SurfaceView 添加到隐藏的 Window:
创建 1x1 像素的隐藏 Window
设置为完全透明(alpha = 0.0f)
移到屏幕外(x = -1000, y = -1000)
使用 TYPE_APPLICATION_OVERLAY 或 TYPE_APPLICATION 类型
在释放资源时移除 Window:确保资源正确释放
2. 在收到onPreviewStarted的时候,延迟1秒进行调用startpushAsync,否则状态不对不会push
2个文件已修改
142 ■■■■ 已修改文件
app/src/main/java/com/safeluck/floatwindow/MainActivity.kt 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/safeluck/floatwindow/manager/UsbCameraPushManager.java 140 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/safeluck/floatwindow/MainActivity.kt
@@ -178,7 +178,7 @@
                codeRate = 0
                frameRate = 0
                m_screen = MediaArgu.ScreenSolution(640, 480) // 默认分辨率
                url = "rtmp://your-push-url" // TODO: 需要设置实际的推流地址
                url = "rtmp://192.168.16.143/live/livestream" // TODO: 需要设置实际的推流地址
                userName = ""
                pwd = ""
            }
app/src/main/java/com/safeluck/floatwindow/manager/UsbCameraPushManager.java
@@ -1,6 +1,11 @@
package com.safeluck.floatwindow.manager;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.WindowManager;
import com.alivc.live.pusher.AlivcAudioAACProfileEnum;
import timber.log.Timber;
@@ -35,12 +40,14 @@
    // 阿里推流相关
    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;
@@ -50,6 +57,9 @@
    
    // 是否开启摄像头加密
    private boolean ay_encrypt = false;
    // 预览 SurfaceView 和隐藏的 Window
    private WindowManager windowManager;
    
    /**
     * 推流回调接口
@@ -96,6 +106,7 @@
        try {
            // 初始化推流SDK
            initAlivcPusher();
            pushStarted = false;
            
            // 检查并打开USB摄像头
            if (!openUsbCamera()) {
@@ -106,11 +117,9 @@
            
            cameraExists = true;
            Timber.d("USB摄像头打开成功");
            
            // 启动摄像头数据推送线程
            startPushThread();
            notifyCallback(1, 0, "推流已启动");
            notifyCallback(1, 0, "推流线程已启动,等待推流状态就绪");
        } catch (Exception e) {
            Timber.e(e, "Failed to start push");
            notifyCallback(1, -3, "启动推流失败: " + e.getMessage());
@@ -127,6 +136,7 @@
        if (usbCamera != null) {
            usbCamera.stopCamera();
        }
        pushStarted = false;
        notifyCallback(1, 4, "推流已停止");
    }
    
@@ -171,7 +181,72 @@
            // 初始化推流器
            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
                public void surfaceCreated(SurfaceHolder holder) {
                    try {
                        Timber.d("previewSurfaceView surfaceCreated, startPreviewAysnc");
                        if (alivcPusher != null) {
                            alivcPusher.startPreviewAysnc(previewSurfaceView);
                            // 启动摄像头数据推送线程
                            startPushThread();
                        }
                    } catch (Exception e) {
                        Timber.e(e, "startPreviewAysnc in surfaceCreated failed");
                        notifyCallback(1, -3, "预览启动失败: " + e.getMessage());
                    }
                }
                @Override
                public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
                    Timber.d("previewSurfaceView surfaceChanged: %dx%d", width, height);
                }
                @Override
                public void surfaceDestroyed(SurfaceHolder holder) {
                    Timber.d("previewSurfaceView surfaceDestroyed");
                }
            });
            // 将 SurfaceView 添加到隐藏的 Window 中,这样 Surface 才会被创建
            WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                    1, 1, // 1x1 像素,几乎不可见
                    WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                            | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
                            | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
                            | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
                    android.graphics.PixelFormat.TRANSLUCENT
            );
            params.x = -1000; // 移到屏幕外
            params.y = -1000;
            params.alpha = 0.0f; // 完全透明
            try {
                windowManager.addView(previewSurfaceView, params);
                Timber.d("previewSurfaceView added to window");
            } catch (Exception e) {
                Timber.e(e, "Failed to add previewSurfaceView to window");
                // 如果添加失败,尝试使用 TYPE_APPLICATION 类型
                params.type = WindowManager.LayoutParams.TYPE_APPLICATION;
                try {
                    windowManager.addView(previewSurfaceView, params);
                    Timber.d("previewSurfaceView added to window with TYPE_APPLICATION");
                } catch (Exception e2) {
                    Timber.e(e2, "Failed to add previewSurfaceView with TYPE_APPLICATION");
                }
            }
            // 设置监听器
            setupListeners();
            
@@ -181,7 +256,8 @@
            notifyCallback(1, -3, "初始化推流SDK失败: " + e.getMessage());
        }
    }
    private Handler mainHandler = new Handler(Looper.getMainLooper());
    /**
     * 设置监听器
     */
@@ -191,22 +267,21 @@
            @Override
            public void onPreviewStarted(AlivcLivePusher alivcLivePusher) {
                Timber.d("onPreviewStarted");
                android.os.Handler handler = new android.os.Handler(android.os.Looper.getMainLooper());
                handler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        if (alivcPusher != null &&
                            alivcPusher.getCurrentStatus() != AlivcLivePushStats.PREVIEWED &&
                            alivcPusher.getCurrentStatus() != AlivcLivePushStats.PUSHED) {
                            Timber.w("Preview状态异常");
                        } else {
                            if (cameraExists && pushUrl != null && !pushUrl.isEmpty()) {
                                Timber.d("开始推流: %s", pushUrl);
                                alivcPusher.startPushAysnc(pushUrl);
                            }
                mainHandler.postDelayed(()->{
                    // 预览就绪后再启动推流,避免 INIT 状态直接 startPush 报错
                    if (alivcPusher != null && pushUrl != null && !pushUrl.isEmpty()) {
                        try {
                            AlivcLivePushStats s = alivcPusher.getCurrentStatus();
                            Timber.i("onPreviewStarted, current status=%s", s != null ? s.name() : "null");
                            Timber.d("开始推流: %s", pushUrl);
                            alivcPusher.startPushAysnc(pushUrl);
                        } catch (Exception e) {
                            Timber.e(e, "startPushAysnc failed");
                            notifyCallback(1, -3, "启动推流失败: " + e.getMessage());
                        }
                    }
                }, 1000);
                },1000);
            }
            
            @Override
@@ -217,6 +292,7 @@
            @Override
            public void onPushStarted(AlivcLivePusher alivcLivePusher) {
                Timber.d("onPushStarted");
                pushStarted = true;
                notifyCallback(1, 0, "推流已开始,分辨率: " + resolutionArr[0] + "x" + resolutionArr[1]);
            }
            
@@ -233,6 +309,7 @@
            @Override
            public void onPushStoped(AlivcLivePusher alivcLivePusher) {
                Timber.d("onPushStoped");
                pushStarted = false;
                notifyCallback(1, 4, "推流已停止");
            }
            
@@ -381,8 +458,8 @@
            // 打开摄像头之前先调用setenv
            usbCamera.setenv();
            // 使用prepareCamera方法,camera_id范围[0,9]
            int[] cameraIds = {0, 9};
            // 使用prepareCamera方法,camera_id范围[0,2]
            int[] cameraIds = {0, 2};
            String cameraName = null; // 不指定特定名称
            // 如果返回非0,代表打开失败,则先stopCamera再重试,最多3次
@@ -437,6 +514,17 @@
     * 释放阿里推流资源
     */
    private void releaseAlivcPusher() {
        // 移除隐藏的 SurfaceView
        if (previewSurfaceView != null && windowManager != null) {
            try {
                windowManager.removeView(previewSurfaceView);
                Timber.d("previewSurfaceView removed from window");
            } catch (Exception e) {
                Timber.e(e, "Error removing previewSurfaceView from window");
            }
            previewSurfaceView = null;
        }
        if (alivcPusher != null) {
            try {
                AlivcLivePushStats stats = alivcPusher.getCurrentStatus();
@@ -453,6 +541,7 @@
            alivcPusher = null;
        }
        alivcLivePushConfig = null;
        pushStarted = false;
    }
    
    /**
@@ -467,6 +556,7 @@
            try {
                int width = resolutionArr[0];
                int height = resolutionArr[1];
                final long startTimeNs = System.nanoTime();
                
                // 计算YUV420缓冲区大小
                int bufferSize = width * height * 3 / 2;
@@ -489,19 +579,23 @@
                    usbCamera.rgba(1, buffer);
                    
                    // 推流数据到阿里云
                    if (alivcPusher != null && cameraExists) {
                    if (alivcPusher != null && cameraExists && pushStarted) {
                        try {
                            long ptsUs = (System.nanoTime() - startTimeNs) / 1000;
                            alivcPusher.inputStreamVideoData(
                                    buffer,
                                    width,
                                    height,
                                    buffer.length,
                                    System.nanoTime() / 1000, // 转换为微秒
                                    ptsUs, // 单调递增的时间戳(微秒)
                                    0 // rotation
                            );
                        } catch (Exception e) {
                            Timber.e(e, "Error pushing frame");
                        }
                    } else if (!pushStarted) {
                        // 等待 onPushStarted 后再喂帧,避免 SDK invalid state
                        Thread.sleep(20);
                    }
                    
                    // 控制帧率,约20fps