Как захватывать и кодировать звук в видеосистеме_Android

Я пытаюсь построить видеосистему с открытым исходным кодом в Android, так как у нас нет доступа к данным в закрытой системе. В этой системе мы можем изменять необработанные данные, снятые камерой.

Я использовал MediaCodec и MediaMux для кодирования и мультиплексирования видеоданных, и это работает. Но я понятия не имею о звуковой части. Я использовал onFramePreview для получения каждого кадра и выполнения кодирования/мультиплексирования по кадрам. Но как мне сделать аудиозапись одновременно (я имею в виду захват звука по кадрам, кодирование его и отправку данных в MediaMux).

Я провел небольшое исследование. Кажется, мы используем аудиорекордер для получения необработанных аудиоданных. Но аудиорекордер выполняет постоянную работу по записи, я не думаю, что он может работать.

Кто-нибудь может подсказать? Спасибо!


person Brendon Tsai    schedule 23.02.2014    source источник


Ответы (1)


Создайте audioRecorder следующим образом:

private AudioRecord getRecorderInstance() {
    AudioRecord ar = null;
    try {
        //Get a audiorecord
        int N = AudioRecord.getMinBufferSize(8000,AudioFormat.CHANNEL_IN_MONO,AudioFormat.ENCODING_PCM_16BIT);            
        ar = new AudioRecord(AudioSource.MIC, 8000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, N*10);
    } 
    catch (Exception e) {

    }
    return ar; //Returns null if mic is unavailable
} 

Подготовьте и отправьте данные для кодирования и мультиплексирования позже, как будто это отдельный поток:

public class MicrophoneInput implements Runnable {
    @Override
    public void run() {
        // Buffer for 200 milliseconds of data, e.g. 400 samples at 8kHz.
        byte[] buffer200ms = new byte[8000 / 10];

        try {
                while (recording) {
                    audioRecorder.read(buffer200ms, 0, buffer200ms.length);

                    //process buffer i.e send to encoder
                    //don't forget to set correct timestamps synchronized with video
                }
        } 
        catch(Throwable x) {        
                //  
        }
    }
}
person Marlon    schedule 24.02.2014
comment
Спасибо, @Марлон. Я пробовал, но столкнулся с некоторыми проблемами. Где я должен разместить функцию audioRecord.startRecording? Я использую функцию обратного вызова onPreviewFrame для получения данных каждого кадра и кодирования/мультиплексирования этих данных. Я пытался поместить startRecording в onPreviewFrame и до onPreviewFrame, но оба варианта работают. Кроме того, я использовал AsyncTask для кодирования фона (onPreivewFrame вызывает эту задачу по кадрам). Если я открою другой поток для audioRecord, как я могу предоставить аудио по кадрам для мультиплексора? Спасибо! - person Brendon Tsai; 25.02.2014
comment
У меня есть audioRecord.startRecording сразу после camera.startPreview(). Основная идея заключается в том, что видео кодируется и отправляется на мультиплексор в потоке камеры. Аудиоцепочка живет в собственном потоке\Runnable, который зацикливается в цикле while{} и считывает аудиосэмплы из audioRecorder, когда сэмплы готовы (здесь 200 мс), а затем отправляются в кодировщик и мультиплексор. Runnable создается\запускается вместе с запуском audioRecord: MicrophoneInput mi = new MicrophoneInput(); audioThread = новый поток (ми); - person Marlon; 25.02.2014
comment
Еще одна мысль: в onPreviewFrame можно добавить объект синхронизации, чтобы нажимать аудиопоток на каждый входящий видеокадр. Таким образом, каждый видеокадр запускал отправку аудиобуфера в кодировщик в отдельном потоке. Но для audioRecorder потребуется более точное выделение буфера. - person Marlon; 25.02.2014
comment
Спасибо. И спасибо за решение обеих моих проблем. Я не понимаю, как отправить буфер из этого потока в onPreiewFrame. И как сделать это асинхронным? Мы просто заставим его отправлять каждые 200 мс (управление по времени) или что-то еще? Не могли бы вы предоставить более подробную информацию? Спасибо! - person Brendon Tsai; 26.02.2014
comment
нужно создать 2 экземпляра медиакодека, один для аудио, другой для видео, правильно их настроить. после отправки данных в видеокодер из onPrevieFrame, в аудиокодер из run() в MicrophoneInput. Просто используйте цикл для получения данных из audioRecorder и отправки в кодировщик. Возможности для улучшения производительности точно есть. И всегда посылайте правильные временные метки на вход энкодера - мультиплексор делает синхронизацию на их основе - person Marlon; 26.02.2014
comment
насколько я понимаю, audioRecorder работает следующим образом: он заполняет внутренние буферы во внутреннем потоке, когда вы запрашиваете audioRecorder.read(buffer200ms, 0, buffer200ms.length); он просто пытается скопировать 200 мс из внутреннего буфера в буфер 200 мс. если во внутреннем буфере меньше 200 мс, он вернет фактический размер скопированных данных. может быть, лучше выделить 1-секундный буфер и сделать чтение каким-то менее частым способом - person Marlon; 26.02.2014
comment
Спасибо за подробное описание. Поэтому мы пишем функцию аудиозаписи и кодирования в run(), а видеозапись и функцию кодирования — в onPreviewFrame. Мы проверяем правильность временных меток для кодировщика, а затем отправляем данные в Muxer. Спасибо! - person Brendon Tsai; 26.02.2014