• 使用javacv录像,同时进行讯飞声纹认证


    由于最近的demo中需要在活体检测的同时进行音视频录制 ,  尝试使用MediaRecord和camera来录制视频 , 然而Camera.onPreviewFrame 不能与 MediaRecord同时调用。活体检测的原理其实是把camera的预览回调onPreviewFrame(byte[] data, Camera camera) 中的图片数据data作为参数传递到活体检测引擎中去拿返回的检测结果码,由于种种原因 , 不能使用Camera2实现 , 于是通过谷歌了解到javacv这个库可以录制视频 , 下了几个demo , 感觉不仅满足需求 , 录制的视频质量也还可以。使用javacv中的FrameRecorder进行录像,录像的时候,调用record方法写帧数据和音频数据,这时候我们有一个需求,录像的同时,要把声音实时拿过来进行声纹认证。由此产生了2个问题:

    问题1:

    语音识别用的是讯飞的SDK,要求声音采样率8k或16k。而设置FrameRecorder.setSampleRate(8000)后,再FrameRecorder.start()会报错,报错如下:

    avcodec_encode_audio2() error 2: Could not encode audio packet.

    问题2:

    javacv官方录制demo中,从AudioRecord中read到的是ShortBuffer,而讯飞SDK方法要求传入byte,他的方法如下:

    public void writeAudio(byte[] data, int start, int length) 

    百度谷歌无果,只好自己研究。

    • 使用javacv进行录像

    下面是使用javacv进行录像的示例代码:

    1. 初始化 ffmpeg_recorder

    public void initRecorder() {
    String ffmpeg_link = parentPath + "/" + "video.mp4";
    Log.w(LOG_TAG, "init recorder");
    
    if (yuvIplimage == null) {
    yuvIplimage = IplImage.create(cameraManager.getDefaultSize().width,
    cameraManager.getDefaultSize().height, IPL_DEPTH_8U, 2);
    Log.i(LOG_TAG, "create yuvIplimage");
    }
    
    Log.i(LOG_TAG, "ffmpeg_url: " + ffmpeg_link);
    recorder = new FFmpegFrameRecorder(ffmpeg_link,
    cameraManager.getDefaultSize().width,
    cameraManager.getDefaultSize().height, 1);
    recorder.setFormat("mp4");
    recorder.setSampleRate(sampleAudioRateInHz);
    // Set in the surface changed method
    recorder.setFrameRate(frameRate);
    
    Log.i(LOG_TAG, "recorder initialize success");
    
    audioRecordRunnable = new AudioRecordRunnable();
    audioThread = new Thread(audioRecordRunnable);
    try {
    recorder.start();
    } catch (Exception e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    audioThread.start();
    }

    2. 捕捉摄像头视频数据:

    public void onPreviewFrame(byte[] data, Camera camera) {
    int during = checkIfMax(new Date().getTime());
    /* get video data */
    if (yuvIplimage != null && isStart) {
    yuvIplimage.getByteBuffer().put(data);
    //yuvIplimage = rotateImage(yuvIplimage.asCvMat(), 90).asIplImage();
    Log.v(LOG_TAG, "Writing Frame");
    try {
    System.out.println(System.currentTimeMillis() - videoStartTime);
    if (during < 6000) {
    recorder.setTimestamp(1000 * during);
    recorder.record(yuvIplimage);
    }
    } catch (FFmpegFrameRecorder.Exception e) {
    Log.v(LOG_TAG, e.getMessage());
    e.printStackTrace();
    }
    }
    }

    3. 捕捉声音数据:

    class AudioRecordRunnable implements Runnable {
    
    @Override
    public void run() {
    android.os.Process
    .setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
    
    // Audio
    int bufferSize;
    short[] audioData;
    int bufferReadResult;
    
    bufferSize = AudioRecord.getMinBufferSize(sampleAudioRateInHz,
    AudioFormat.CHANNEL_CONFIGURATION_MONO,
    AudioFormat.ENCODING_PCM_16BIT);
    audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,
    sampleAudioRateInHz,
    AudioFormat.CHANNEL_CONFIGURATION_MONO,
    AudioFormat.ENCODING_PCM_16BIT, bufferSize);
    
    audioData = new short[bufferSize];
    
    Log.d(LOG_TAG, "audioRecord.startRecording()");
    audioRecord.startRecording();
    
    /* ffmpeg_audio encoding loop */
    while (!isFinished) {
    // Log.v(LOG_TAG,"recording? " + recording);
    bufferReadResult = audioRecord.read(audioData, 0,
    audioData.length);
    if (bufferReadResult > 0) {
    // Log.v(LOG_TAG, "bufferReadResult: " + bufferReadResult);
    // If "recording" isn't true when start this thread, it
    // never get's set according to this if statement...!!!
    // Why? Good question...
    if (isStart) {
    try {
    Buffer[] barray = new Buffer[1];
    barray[0] = ShortBuffer.wrap(audioData, 0,
    bufferReadResult);
    recorder.record(barray);
    // Log.v(LOG_TAG,"recording " + 1024*i + " to " +
    // 1024*i+1024);
    } catch (FFmpegFrameRecorder.Exception e) {
    Log.v(LOG_TAG, e.getMessage());
    e.printStackTrace();
    }
    }
    }
    }
    Log.v(LOG_TAG, "AudioThread Finished, release audioRecord");
    
    /* encoding finish, release recorder */
    if (audioRecord != null) {
    audioRecord.stop();
    audioRecord.release();
    audioRecord = null;
    Log.v(LOG_TAG, "audioRecord released");
    }
    }
    }

    解决问题1:

    demo中默认设置FrameRecorder.setSampleRate(44100)没问题,想到一个办法,这个地方设置44100,在语音采集的地方设置8000,最后成功了。不过这个计算时间的方法要修改:

    public static int getTimeStampInNsFromSampleCounted(int paramInt) {
    // return (int) (paramInt / 0.0441D);
    return (int) (paramInt / 0.0080D);
    }
    
     

    解决问题2:

    short数组转byte数组,注意数组长度变为原来的2倍

    public static byte[] short2byte(short[] sData) {
    int shortArrsize = sData.length;
    byte[] bytes = new byte[shortArrsize * 2];
    
    for (int i = 0; i < shortArrsize; i++) {
    bytes[i * 2] = (byte) (sData[i] & 0x00FF);
    bytes[(i * 2) + 1] = (byte) (sData[i] >> 8);
    sData[i] = 0;
    }
    return bytes;
    
    }

    录制音频源码:

     /**
         * 录制音频的线程
         */
        class AudioRecordRunnable implements Runnable {
            short[] audioData;
            private final AudioRecord audioRecord;
            private int mCount = 0;
            int sampleRate = Constants.AUDIO_SAMPLING_RATE;
     
     
            private AudioRecordRunnable() {
                int bufferSize = AudioRecord.getMinBufferSize(sampleRate,
                        AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
                audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRate,
                        AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize);
                audioData = new short[bufferSize];
     
     
            }
     
     
            /**
             * 包含了音频的数据和起始位置
             *
             * @param buffer
             */
            private void record(Buffer buffer) {
                synchronized (mAudioRecordLock) {
                    this.mCount += buffer.limit();
                    if (!mIsPause) {
                        try {
                            if (mRecorder != null) {
                                mRecorder.record(sampleRate, new Buffer[]{buffer});
                            }
                        } catch (FrameRecorder.Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
     
     
            /**
             * 更新音频的时间戳
             */
            private void updateTimestamp() {
                int i = Util.getTimeStampInNsFromSampleCounted(this.mCount);
                if (mAudioTimestamp != i) {
                    mAudioTimestamp = i;
                    mAudioTimeRecorded = System.nanoTime();
                }
            }
     
     
            public void run() {
                android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
                if (audioRecord != null) {
                    //判断音频录制是否被初始化
                    while (this.audioRecord.getState() == 0) {
                        try {
                            Thread.sleep(100L);
                        } catch (InterruptedException localInterruptedException) {
                        }
                    }
                    this.audioRecord.startRecording();
                    while ((runAudioThread)) {
                        updateTimestamp();
                        int bufferReadResult = this.audioRecord.read(audioData, 0, audioData.length);
                        if (bufferReadResult > 0) {
                            if (recording || (mVideoTimestamp > mAudioTimestamp)) {
                                record(ShortBuffer.wrap(audioData, 0, bufferReadResult));
                            }
                            if (SpeechManager.getInstance().isListening()) {
                                SpeechManager.getInstance().writeAudio(Util.short2byte(audioData), 0, bufferReadResult * 2);
                            }
                        }
                    }
                    SpeechManager.getInstance().stopListener();
                    this.audioRecord.stop();
                    this.audioRecord.release();
                }
            }
        }
  • 相关阅读:
    UI系统的分类
    DSL的概念
    什么是“图灵完备”?
    UI系统的作用
    redis——持久化方式RDB与AOF分析
    Redis能做什么?不能做什么?
    PHP Ajax 跨域问题最佳解决方案
    charles和Fiddler感觉哪个更好用
    Fiddler工具使用介绍一
    Xshell出现要继续使用此程序必须应用到最新的更新或使用新版本
  • 原文地址:https://www.cnblogs.com/fuyaozhishang/p/9789193.html
Copyright © 2020-2023  润新知