IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android12 Native C++ 层AudioRecord录音AudioTrack播放 -> 正文阅读

[移动开发]Android12 Native C++ 层AudioRecord录音AudioTrack播放

一、背景:

? ? ? 发现网上能搜到的都是几年前的例子,现在Android版本都出到12了,几年前的例子以及不适用新版本了。于是就自己研究了下AudioRecord.java是如何调用到jni和C++层的AudioRecord.cpp。做一下记录为后来者避坑(PS:以前都不喜欢写博客,本人没时间也不太想分享)。改demo应该适用于Android8,Android9,Android10,Android11,Android12。或许过个几年到Androidxx又不适用了。

二、遇到比较有意思的坑:

程序运行没有报错但是录音没有声音,dump文件拉到电脑播放也是一条直线没有声音。后来分析是google基于安全 权限考虑做的限制。会根据uid,ui是否在前台等对AudioRecord进行静音。

解决方法有两个:
1. 注释掉:void AudioFlinger::setRecordSilenced(audio_port_handle_t portId, bool silenced)函数静音代码。

2. 使用 audio_source_t inputSource =AUDIO_SOURCE_HOTWORD;//AUDIO_SOURCE_MIC;

在老版本Android是使用AUDIO_SOURCE_MIC类型的。这里因为是c++ native没有ui界面等所以必须使用AUDIO_SOURCE_HOTWORD类型,否则 AudioRecord录音没有声音。

三、Demo源码:

例子有点乱,没有整理。AudioTrack是基于framework目录下AudioTrack共享内存例子修改的。

AudioRecord没有原生例子,只能自己研究了。

demo_test:


#define LOG_NDEBUG 0
#define LOG_TAG "demo_test"
 
#include <stdlib.h>
#include <stdio.h>
#include <cutils/properties.h>
#include <media/AudioSystem.h>
#include <media/AudioTrack.h>
#include <media/AudioRecord.h>
#include <binder/ProcessState.h>
#include <binder/IPCThreadState.h>
#include <media/AidlConversion.h>
 
#include <utils/Log.h>
 
#include <fcntl.h>
#include "demo_test.h"
#define SOUND_LENGTH        18470
volatile bool                         g_bQuitAudioRecordThread = false;
int                                                                 g_iNotificationPeriodInFrames = 8000/10;
void StartAudioRecordThread();
void StopAudioRecordThread();
void *        inBuffer = NULL;
int myreadlen = 0;
int mywritelen = 0;
int                                                                         bufferSizeInBytes = 1600;
#define SAMPLE_RATE 16000
 
// 这里必须使用 AUDIO_SOURCE_HOTWORD 否则录音无声,或者把 void AudioFlinger::setRecordSilenced(audio_port_handle_t portId, bool silenced)
//函数注释为空。
audio_source_t                                 inputSource                         = AUDIO_SOURCE_HOTWORD;//AUDIO_SOURCE_HOTWORD;//AUDIO_SOURCE_MIC;
static const audio_attributes_t pAttributes = {
/* .content_type = */ AUDIO_CONTENT_TYPE_UNKNOWN, // 0
/* .usage = */ (audio_usage_t)AUDIO_USAGE_UNKNOWN, // 0
/* .source = */ inputSource, // 1
/* .flags = */ AUDIO_FLAG_MUTE_HAPTIC,// 0x00000800 //AUDIO_FLAG_NONE,
/* .tags = */ "demo_test"
};
static android::content::AttributionSourceState attributionSource;
 
namespace android {
 
void AudioRecordCallback(int event, void* user, void *info)
{
        char *p;
        if(user)
                p = (char *)user;
        if(event == android::AudioRecord::EVENT_NEW_POS)
        {       
                ALOGD(" android::AudioRecord::EVENT_NEW_POS \n");
                //if(g_iInSampleTime > g_iNotificationPeriodInFrames*100)
                //                g_bQuitAudioRecordThread = true;
        }
        else if (event == android::AudioRecord::EVENT_MORE_DATA)
        {               
                ALOGD(" android::AudioRecord::EVENT_MORE_DATA..... \n");
                android::AudioRecord::Buffer* pBuff = (android::AudioRecord::Buffer*)info;
                pBuff->size = 0;
        }
        else if (event == android::AudioRecord::EVENT_OVERRUN)
        {
                ALOGD(" EVENT_OVERRUN \n");
        }
}
#if 1
static void AudioRecordThread()
{
       
        audio_format_t                                 audioFormat                         = AUDIO_FORMAT_PCM_16_BIT;        // 1
        audio_channel_mask_t         channelConfig                 = AUDIO_CHANNEL_IN_MONO;//AUDIO_CHANNEL_IN_MONO; //AUDIO_CHANNEL_IN_STEREO;       
       
        int                                                                         sampleRateInHz                 = SAMPLE_RATE;       
        sp<AudioRecord> pAudioRecord = 0;
        FILE *                                                                         g_pAudioRecordFile                 = NULL;
        char                                                                                 strAudioFile[]                                 = "/sdcard/AudioRecordFile.pcm";
        int iNbChannels                 = 1;        // 1 channel for mono, 2 channel for streo
        int iBytesPerSample = 2;         // 16bits pcm, 2Bytes
        int frameSize                         = 0;        // frameSize = iNbChannels * iBytesPerSample
    size_t minFrameCount         = 0;        // get from AudroRecord object
 
        g_pAudioRecordFile = fopen(strAudioFile, "wb+");       
       
 
               
        iNbChannels = (channelConfig == AUDIO_CHANNEL_IN_STEREO) ? 2 : 1;
        frameSize         = iNbChannels * iBytesPerSample;       
        android::status_t         status = AudioRecord::getMinFrameCount(
                &minFrameCount, sampleRateInHz, audioFormat, channelConfig);       
       
        if(status != android::NO_ERROR)
        {
                ALOGD("%s  AudioRecord.getMinFrameCount fail \n", __FUNCTION__);
        }
        // sampleRateInHz = 16000 minFrameCount = 768 iNbChannels = 2 frameSize = 4
        ALOGD("sampleRateInHz = %d minFrameCount = %d iNbChannels = %d frameSize = %d ",
                sampleRateInHz, (int)minFrameCount, iNbChannels, frameSize);       
       
        bufferSizeInBytes = (int)minFrameCount * frameSize;
        //bufferSizeInBytes =  2*SOUND_LENGTH;
 
        g_iNotificationPeriodInFrames = sampleRateInHz/10;       
       
       
        String16 opPackageName("demo_test");
        uid_t uid = -1;
        pid_t pid = -1;
    attributionSource.packageName = VALUE_OR_FATAL(legacy2aidl_String16_string(opPackageName));
    attributionSource.uid = VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t((unsigned int)uid));
    attributionSource.pid = VALUE_OR_FATAL(legacy2aidl_pid_t_int32_t((int)pid));
    attributionSource.token = sp<BBinder>::make();
 
       
        pAudioRecord  = new AudioRecord(attributionSource);       
 
        if(NULL == pAudioRecord)
        {
                ALOGD(" create native AudioRecord failed! ");
        }                       
        audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE;// 0
 
                audio_session_t sessionId = (audio_session_t)0;
                int sharedAudioHistoryMs  = 0;
        status_t status2 =
                        pAudioRecord->set(pAttributes.source, sampleRateInHz,
                                                        audioFormat, // word length, PCM
                                                        channelConfig, 768,
                                                        AudioRecordCallback, // callback_t
                                                        NULL,   // void* user
                                                        0,                // notificationFrames,
                                                        true,             // threadCanCallJava
                                                        sessionId, AudioRecord::TRANSFER_DEFAULT, flags, -1,
                                                        -1, // default uid, pid
                                                        &pAttributes, AUDIO_PORT_HANDLE_NONE, MIC_DIRECTION_UNSPECIFIED,
                                                        MIC_FIELD_DIMENSION_DEFAULT, sharedAudioHistoryMs);
 
        if (status2 != NO_ERROR) {
            ALOGE("Error creating AudioRecord instance: initialization check failed with status2 %d.",
                    status2);
        }
        pAudioRecord->setCallerName("demo_test");                               
                                                               
        if(pAudioRecord->initCheck() != android::NO_ERROR) 
        {
                ALOGD("AudioTrack initCheck error!");
        }
        if(pAudioRecord->setPositionUpdatePeriod(g_iNotificationPeriodInFrames) != android::NO_ERROR)
        {
                ALOGD("AudioTrack setPositionUpdatePeriod error!");
        }
       
        if(pAudioRecord->start()!= android::NO_ERROR)
        {
                ALOGD("AudioTrack start error!");
        }       
        int i = 0;
        int readLen = 0;
        while (!g_bQuitAudioRecordThread)
        {
 
                readLen = pAudioRecord->read((uint8_t*)inBuffer+i*readLen, 320/*bufferSizeInBytes*/, 1);
                int writeResult = -1;
               
                if(readLen > 0)
                {
                       
                        myreadlen += readLen;
                        if ( myreadlen > 7*SOUND_LENGTH ) {
                                ALOGD("pAudioRecord->read  end");
                                break;
                        }
                        //iWriteDataCount += readLen;
                        if(NULL != g_pAudioRecordFile)
                        {
                                writeResult = fwrite((uint8_t*)inBuffer+i*readLen, 1, readLen, g_pAudioRecordFile);                               
                                if(writeResult < readLen)
                                {
                                        ALOGE("Write Audio Record Stream error");
                                }
                                mywritelen += writeResult;
                        }
                        i++;       
                }
                else
                {
                        ALOGD("pAudioRecord->read  readLen = 0");
                }
        }
        ALOGD("myreadlen = %d  mywritelen = %d\n", myreadlen, mywritelen);       
       
        if(pAudioRecord)
        {
                pAudioRecord->stop();
        }
        if(NULL != g_pAudioRecordFile)
        {
                fflush(g_pAudioRecordFile);
                fclose(g_pAudioRecordFile);               
                g_pAudioRecordFile = NULL;               
        }
}
 
#endif
 
 
/************************************************************
*
*    Constructor
*
************************************************************/
AudioTrackTest::AudioTrackTest(void) {
 
   ALOGD("01 AudioTrackTest\n");
 
}
 
 
/************************************************************
*
*
************************************************************/
void AudioTrackTest::Execute(void) {
    if (Test01() == 0) {
        ALOGD("01 passed\n");
    } else {
        ALOGD("01 failed\n");
    }
}
 
/************************************************************
*
*    Shared memory test
*
************************************************************/
#define BUF_SZ 44100
 
int AudioTrackTest::Test01() {
        bufferSizeInBytes =  10*SOUND_LENGTH;
        inBuffer = malloc(bufferSizeInBytes);
        if(inBuffer == NULL)
        {               
                ALOGD("%s  alloc mem failed \n", __FUNCTION__);               
        }
        memset(inBuffer, 0, bufferSizeInBytes);
        uint8_t* buf = (uint8_t*)inBuffer;
    long sampleRate = SAMPLE_RATE;
 
        size_t framecount = 16;
 
        AudioRecordThread();
 
        if (AudioTrack::getMinFrameCount(&framecount, AUDIO_STREAM_MUSIC, sampleRate) != NO_ERROR ) {
                ALOGD("Error: cannot compute output frame count\n");
                return -1;
        }
        ALOGD("framecount = %d \n",  (int)framecount);
        sp<AudioTrack> track = new AudioTrack(AUDIO_STREAM_MUSIC,// stream type
                   sampleRate,
                   AUDIO_FORMAT_PCM_16_BIT,// word length, PCM
                   AUDIO_CHANNEL_OUT_MONO,  // AUDIO_CHANNEL_OUT_STEREO
                   0);
 
        status_t status = track->initCheck();
        if(status != NO_ERROR) {
                track.clear();
                ALOGD("Failed for initCheck()");
                return -1;
        }
 
        // start play
        ALOGD("start");
        track->start();
        for(int j = 0; j < 3; j++)
        for(int i = 0; i <= 7*SOUND_LENGTH/320 ; i++){
                track->write(buf+i*320, 320, 1);
        }
 
        ALOGD("stop");
        track->stop();
        if(inBuffer)
        {
                free(inBuffer);
                inBuffer = NULL;
        }
    return 0;
 
}
 
 
 
/************************************************************
*
*    main in name space
*
************************************************************/
int main() {
    ProcessState::self()->startThreadPool();
    AudioTrackTest *test;
 
    test = new AudioTrackTest();
    test->Execute();
    delete test;
 
    return 0;
}
 
}
 
/************************************************************
*
*    global main
*
************************************************************/
int main() {
 
    return android::main();
}
 

demo_test.h


// Copyright 2008 The Android Open Source Project
 
#ifndef AUDIOTRACKTEST_H_
#define AUDIOTRACKTEST_H_
 
namespace android {
 
class AudioTrackTest{
    public:
        AudioTrackTest(void);
        ~AudioTrackTest() {};
 
        void Execute(void);
        int Test01();
 
};
 
};
 
 
#endif /*AUDIOTRACKTEST_H_*/
 

Android.bp


cc_test {
    name: "demo_test",
    gtest: false,
 
    srcs: ["demo_test.cpp"],
 
    shared_libs: [
        "liblog",
        "libcutils",
        "libutils",
        "libbinder",
        "libhardware_legacy",
        "libmedia",
        "libaudioclient",
        "libaudioclient_aidl_conversion",
    ],
 
    header_libs: [
        "libmediametrics_headers",
    ],
 
}
 

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-04-13 23:35:31  更:2022-04-14 00:18:56 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 22:08:17-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码