Dana
7 天以前 fa4b2286ed45545efeee41d10e7a5bac76811232
1.usb 推流添加声音
1个文件已添加
1个文件已修改
180 ■■■■■ 已修改文件
app/src/main/java/com/safeluck/floatwindow/manager/UsbCameraPushManager.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/safeluck/floatwindow/util/AudioRecordManager.java 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/safeluck/floatwindow/manager/UsbCameraPushManager.java
@@ -7,6 +7,9 @@
import android.view.SurfaceView;
import android.view.WindowManager;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.alivc.live.pusher.AlivcAudioAACProfileEnum;
import timber.log.Timber;
import com.alivc.live.pusher.AlivcEncodeModeEnum;
@@ -26,6 +29,7 @@
import com.anyun.libusbcamera.UsbCamera;
import com.safeluck.floatwindow.MediaArgu;
import com.safeluck.floatwindow.ResponseVO;
import com.safeluck.floatwindow.util.AudioRecordManager;
/**
 * USB摄像头推流管理器
@@ -60,6 +64,9 @@
    
    // 预览 SurfaceView 和隐藏的 Window
    private WindowManager windowManager;
    // 音频推流线程池(单线程)
    private ExecutorService audioPushExecutor;
    
    /**
     * 推流回调接口
@@ -132,6 +139,7 @@
    public void stopPush() {
        Timber.d("stopPush called");
        stopPushThread();
        stopAudioTransfer();
        releaseAlivcPusher();
        if (usbCamera != null) {
            usbCamera.stopCamera();
@@ -293,6 +301,7 @@
            public void onPushStarted(AlivcLivePusher alivcLivePusher) {
                Timber.d("onPushStarted");
                pushStarted = true;
                startAudioTransfer();
                notifyCallback(1, 0, "推流已开始,分辨率: " + resolutionArr[0] + "x" + resolutionArr[1]);
            }
            
@@ -624,4 +633,52 @@
            callback.onResult(response);
        }
    }
    private void startAudioTransfer() {
        Timber.i("开始通过mic录制声音,上传");
        // 创建单线程线程池用于音频推流
        if (audioPushExecutor == null || audioPushExecutor.isShutdown()) {
            audioPushExecutor = Executors.newSingleThreadExecutor(r -> {
                Thread thread = new Thread(r, "AudioPushThread");
                thread.setDaemon(true);
                return thread;
            });
        }
        AudioRecordManager.getInstance().startRecording((data, size) -> {
            if (alivcPusher != null && audioPushExecutor != null && !audioPushExecutor.isShutdown()) {
                // 在单线程线程池中执行音频推流
                audioPushExecutor.execute(() -> {
                    try {
                        alivcPusher.inputStreamAudioData(data, data.length, System.nanoTime() / 1000);
                    } catch (Exception e) {
                        Timber.e(e, "Error pushing audio data");
                    }
                });
            }
        });
    }
    private void stopAudioTransfer() {
        Timber.i("停止通过mic录制声音,上传");
        AudioRecordManager.getInstance().stopRecording();
        // 停止并关闭音频推流线程池
        if (audioPushExecutor != null && !audioPushExecutor.isShutdown()) {
            audioPushExecutor.shutdown();
            try {
                // 等待最多1秒让任务完成
                if (!audioPushExecutor.awaitTermination(1, java.util.concurrent.TimeUnit.SECONDS)) {
                    audioPushExecutor.shutdownNow();
                }
            } catch (InterruptedException e) {
                audioPushExecutor.shutdownNow();
                Thread.currentThread().interrupt();
            }
            audioPushExecutor = null;
            Timber.d("音频推流线程池已关闭");
        }
    }
}
app/src/main/java/com/safeluck/floatwindow/util/AudioRecordManager.java
New file
@@ -0,0 +1,123 @@
package com.safeluck.floatwindow.util;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Environment;
import android.util.Log;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
 * aaa
 * Created by lzw on 2019/8/29. 11:19:57
 * 邮箱:632393724@qq.com
 * All Rights Saved! Chongqing AnYun Tech co. LTD
 */
public class AudioRecordManager {
    private static final String TAG = "AudioRecordManager";
    private static AudioRecordManager instance;
    /***标记是否正在录音**/
    private boolean isRecording = false;
    private AudioRecord audioRecord;
    /***最小缓冲区大小*/
    private int bufferSize = 0;
    /***采样率*/
    private int sampleRateInHz = 32000;
    /***量化位数**/
    private int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
    /***存放音频数据的buffer**/
    private byte[] buffer;
    private int channelConf = AudioFormat.CHANNEL_IN_STEREO;
    private OnAudioRecordListener onAudioRecordListener;
    private AudioRecordManager() {
        //计算最小缓冲区
        bufferSize = AudioRecord.getMinBufferSize(sampleRateInHz, channelConf, audioFormat);
//        bufferSize = bufferSize > 320 ? 320 : bufferSize;
        Log.i(TAG,"mini buffersize="+bufferSize);
        audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRateInHz, channelConf, audioFormat, bufferSize);
    }
    public static AudioRecordManager getInstance() {
        if (instance == null) {
            synchronized (AudioRecordManager.class) {
                if (instance == null) {
                    instance = new AudioRecordManager();
                }
            }
        }
        return instance;
    }
    /***
     * 开始采集音频
     */
    public void startRecording(final OnAudioRecordListener onAudioRecordListener) {
        setAudioRecordListener(onAudioRecordListener);
        if (audioRecord == null){
            audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRateInHz, channelConf, audioFormat, bufferSize);
        }
        buffer = new byte[bufferSize];
                isRecording = true;
                audioRecord.startRecording();
                try {
                    while (isRecording){
                        int readSize = audioRecord.read(buffer,0,bufferSize);
                        Log.i(TAG, "run: buffer length"+buffer.length+" readSize="+readSize);
                        if (onAudioRecordListener != null){
                            onAudioRecordListener.onVoiceRecord(buffer,bufferSize);
                        }
                    }
                    audioRecord.stop();
                    destroy();
                } catch (Exception e) {
                    e.printStackTrace();
                }
    }
    /***
     * 停止音频采集
     */
    public void stopRecording(){
        Log.i(TAG, "stopRecording");
        isRecording = false;
        setAudioRecordListener(null);
    }
    public void destroy(){
        if (audioRecord!=null){
            audioRecord.release();
            audioRecord = null;
        }
    }
    public interface OnAudioRecordListener {
        /***
         * 采集到的音频信息回调到上层
         * @param data
         * @param size
         */
        void onVoiceRecord(byte[] data,int size);
    }
    private void setAudioRecordListener(OnAudioRecordListener onAudioRecordListener){
        this.onAudioRecordListener = onAudioRecordListener;
    }
}