| | |
| | | // 音频推流线程池(单线程) |
| | | private ExecutorService audioPushExecutor; |
| | | |
| | | // 保护 alivcPusher / previewSurfaceView 等生命周期,避免 start/stop 并发导致 NPE |
| | | private final Object pusherLock = new Object(); |
| | | |
| | | /** |
| | | * 推流回调接口 |
| | | */ |
| | |
| | | * 停止推流 |
| | | */ |
| | | public void stopPush() { |
| | | Timber.d("%s stopPush called", getCameraTag()); |
| | | stopPushThread(); |
| | | // stopAudioTransfer(); |
| | | stopWaterMaskSchedule(); |
| | | releaseAlivcPusher(); |
| | | if (usbCamera != null) { |
| | | usbCamera.stopCamera(); |
| | | // stop 同样强制在主线程串行执行,避免与 init/setupListeners 并发 |
| | | if (Looper.myLooper() == Looper.getMainLooper()) { |
| | | stopPushInternal(); |
| | | } else { |
| | | mainHandler.post(this::stopPushInternal); |
| | | } |
| | | pushStarted = false; |
| | | notifyCallback(1, 4, "推流已停止"); |
| | | } |
| | | |
| | | private void stopPushInternal() { |
| | | synchronized (pusherLock) { |
| | | Timber.d("%s stopPush called", getCameraTag()); |
| | | stopPushThread(); |
| | | // stopAudioTransfer(); |
| | | stopWaterMaskSchedule(); |
| | | releaseAlivcPusherLocked(); |
| | | if (usbCamera != null) { |
| | | usbCamera.stopCamera(); |
| | | } |
| | | pushStarted = false; |
| | | notifyCallback(1, 4, "推流已停止"); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 初始化阿里推流 |
| | | */ |
| | | private void initAlivcPusher() { |
| | | try { |
| | | alivcLivePushConfig = new AlivcLivePushConfig(); |
| | | synchronized (pusherLock) { |
| | | try { |
| | | alivcLivePushConfig = new AlivcLivePushConfig(); |
| | | |
| | | // 根据分辨率设置 |
| | | setResolutionFromArray(resolutionArr); |
| | | // 根据分辨率设置 |
| | | setResolutionFromArray(resolutionArr); |
| | | |
| | | // 建议用户使用20fps |
| | | alivcLivePushConfig.setFps(AlivcFpsEnum.FPS_20); |
| | | // 建议用户使用20fps |
| | | alivcLivePushConfig.setFps(AlivcFpsEnum.FPS_20); |
| | | |
| | | // 打开码率自适应 |
| | | alivcLivePushConfig.setEnableBitrateControl(true); |
| | | // 打开码率自适应 |
| | | alivcLivePushConfig.setEnableBitrateControl(true); |
| | | |
| | | // 设置横屏方向 |
| | | alivcLivePushConfig.setPreviewOrientation(AlivcPreviewOrientationEnum.ORIENTATION_LANDSCAPE_HOME_LEFT); |
| | | // 设置横屏方向 |
| | | alivcLivePushConfig.setPreviewOrientation(AlivcPreviewOrientationEnum.ORIENTATION_LANDSCAPE_HOME_LEFT); |
| | | |
| | | // 设置音频编码模式 |
| | | alivcLivePushConfig.setAudioProfile(AlivcAudioAACProfileEnum.AAC_LC); |
| | | // 设置音频编码模式 |
| | | alivcLivePushConfig.setAudioProfile(AlivcAudioAACProfileEnum.AAC_LC); |
| | | |
| | | // 设置摄像头类型 |
| | | alivcLivePushConfig.setCameraType(AlivcLivePushCameraTypeEnum.CAMERA_TYPE_BACK); |
| | | // 设置摄像头类型 |
| | | alivcLivePushConfig.setCameraType(AlivcLivePushCameraTypeEnum.CAMERA_TYPE_BACK); |
| | | |
| | | // 设置视频编码模式为硬编码 |
| | | alivcLivePushConfig.setVideoEncodeMode(AlivcEncodeModeEnum.Encode_MODE_HARD); |
| | | // 设置视频编码模式为硬编码 |
| | | alivcLivePushConfig.setVideoEncodeMode(AlivcEncodeModeEnum.Encode_MODE_HARD); |
| | | |
| | | // 关闭美颜 |
| | | alivcLivePushConfig.setBeautyOn(false); |
| | | // 关闭美颜 |
| | | alivcLivePushConfig.setBeautyOn(false); |
| | | |
| | | // 清晰度优先模式 |
| | | alivcLivePushConfig.setQualityMode(AlivcQualityModeEnum.QM_RESOLUTION_FIRST); |
| | | // 清晰度优先模式 |
| | | alivcLivePushConfig.setQualityMode(AlivcQualityModeEnum.QM_RESOLUTION_FIRST); |
| | | |
| | | // 设置自定义流模式 |
| | | alivcLivePushConfig.setExternMainStream(true); |
| | | alivcLivePushConfig.setAlivcExternMainImageFormat(AlivcImageFormat.IMAGE_FORMAT_YUV420P); |
| | | // 设置自定义流模式 |
| | | 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()); |
| | | // 初始化推流器:先用局部变量,避免中途被 stop 置空导致 setupListeners NPE |
| | | AlivcLivePusher localPusher = new AlivcLivePusher(); |
| | | localPusher.init(context.getApplicationContext(), alivcLivePushConfig); |
| | | |
| | | // 在 SurfaceView 的 surfaceCreated 回调中再启动预览,确保 Surface 已经创建 |
| | | previewSurfaceView.getHolder().addCallback(new SurfaceHolder.Callback() { |
| | | @Override |
| | | public void surfaceCreated(SurfaceHolder holder) { |
| | | try { |
| | | Timber.d("%s previewSurfaceView surfaceCreated, startPreviewAysnc", getCameraTag()); |
| | | if (alivcPusher != null) { |
| | | alivcPusher.startPreviewAysnc(previewSurfaceView); |
| | | // 外部自定义流模式下,同样需要先开启预览,让状态从 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("%s previewSurfaceView surfaceCreated, startPreviewAysnc", getCameraTag()); |
| | | synchronized (pusherLock) { |
| | | if (alivcPusher != null && previewSurfaceView != null) { |
| | | alivcPusher.startPreviewAysnc(previewSurfaceView); |
| | | |
| | | |
| | | // 启动摄像头数据推送线程 |
| | | startPushThread(); |
| | | } |
| | | } catch (Exception e) { |
| | | // 启动摄像头数据推送线程 |
| | | startPushThread(); |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | | Timber.e(e, "%s startPreviewAysnc in surfaceCreated failed", getCameraTag()); |
| | | notifyCallback(1, -3, "预览启动失败: " + e.getMessage()); |
| | | 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"); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { |
| | | Timber.d("previewSurfaceView surfaceChanged: %dx%d", width, height); |
| | | } |
| | | // 设置监听器(对局部 pusher 先绑定),最后再发布到字段 |
| | | setupListeners(localPusher); |
| | | alivcPusher = localPusher; |
| | | |
| | | @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"); |
| | | Timber.d("%s AlivcPusher initialized successfully", getCameraTag()); |
| | | } 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"); |
| | | } |
| | | Timber.e(e, "%s Failed to initialize AlivcPusher", getCameraTag()); |
| | | notifyCallback(1, -3, "初始化推流SDK失败: " + e.getMessage()); |
| | | } |
| | | |
| | | // 设置监听器 |
| | | setupListeners(); |
| | | |
| | | Timber.d("%s AlivcPusher initialized successfully", getCameraTag()); |
| | | } catch (Exception e) { |
| | | Timber.e(e, "%s Failed to initialize AlivcPusher", getCameraTag()); |
| | | notifyCallback(1, -3, "初始化推流SDK失败: " + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | |
| | | /** |
| | | * 设置监听器 |
| | | */ |
| | | private void setupListeners() { |
| | | private void setupListeners(AlivcLivePusher pusher) { |
| | | if (pusher == null) { |
| | | Timber.w("%s setupListeners skipped: pusher is null", getCameraTag()); |
| | | return; |
| | | } |
| | | // 推流信息监听器 |
| | | alivcPusher.setLivePushInfoListener(new AlivcLivePushInfoListener() { |
| | | pusher.setLivePushInfoListener(new AlivcLivePushInfoListener() { |
| | | @Override |
| | | public void onPreviewStarted(AlivcLivePusher alivcLivePusher) { |
| | | Timber.d("%s onPreviewStarted", getCameraTag()); |
| | |
| | | }); |
| | | |
| | | // 错误监听器 |
| | | alivcPusher.setLivePushErrorListener(new AlivcLivePushErrorListener() { |
| | | pusher.setLivePushErrorListener(new AlivcLivePushErrorListener() { |
| | | @Override |
| | | public void onSystemError(AlivcLivePusher alivcLivePusher, AlivcLivePushError alivcLivePushError) { |
| | | Timber.e("onSystemError: %s", alivcLivePushError.toString()); |
| | |
| | | }); |
| | | |
| | | // 网络监听器 |
| | | alivcPusher.setLivePushNetworkListener(new AlivcLivePushNetworkListener() { |
| | | pusher.setLivePushNetworkListener(new AlivcLivePushNetworkListener() { |
| | | @Override |
| | | public void onNetworkPoor(AlivcLivePusher alivcLivePusher) { |
| | | Timber.w("onNetworkPoor"); |
| | |
| | | * 释放阿里推流资源 |
| | | */ |
| | | private void releaseAlivcPusher() { |
| | | synchronized (pusherLock) { |
| | | releaseAlivcPusherLocked(); |
| | | } |
| | | } |
| | | |
| | | private void releaseAlivcPusherLocked() { |
| | | // 兜底:防止外部没有走 stopPush |
| | | stopWaterMaskSchedule(); |
| | | // 移除隐藏的 SurfaceView |
| | |
| | | Timber.d("当前推流状态: %s", stats != null ? stats.name() : "null"); |
| | | |
| | | if (stats != null && (stats == AlivcLivePushStats.PUSHED || |
| | | stats == AlivcLivePushStats.PREVIEWED)) { |
| | | stats == AlivcLivePushStats.PREVIEWED)) { |
| | | alivcPusher.stopPush(); |
| | | } |
| | | alivcPusher.destroy(); |