Android JNI实现录音和播放

该博客主要展示了如何使用JNI接口结合OpenSL在Android中实现录音和播放功能。通过C/C++的本地代码,实现了录音的开始、停止以及播放的控制,并对录音文件进行读取和播放。同时,代码中还涉及到了时间限制的设置方法。
摘要由CSDN通过智能技术生成

1.jni 部分开发,实现录音和播放
2.实现so库有效时间限制

	/**
	 * 引用和声明JNI库和函数的类
	 */
	public class RecoderJni {
	    static {
	        System.loadLibrary("recoder-lib");
	    }
	
	    //native interface
	    public native int play(String filePath);
	
	    public native int playStop();
	
	    public native int record(String filePath);
	
	    public native int stopRecod();
	
	    public native int setAvailableTime(long avatime);
	
	    public native long getSetTime();
	}

代码 opensl.h:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#include <SLES/OpenSLES.h>
/* Header for class com_test_recoder_RecoderJni */

#ifndef _Included_com_test_recoder_RecoderJni
#define _Included_com_test_recoder_RecoderJni
#ifdef __cplusplus
extern "C" {
#endif

/*
 * Class:     com_test_recoder_RecoderJni
 * Method:    play
 * Signature: (Ljava/lang/String;)I //函数签名
 */
JNIEXPORT jint JNICALL Java_com_test_recoder_RecoderJni_play
        (JNIEnv *, jobject, jstring);

/*
 * Class:     com_test_recoder_RecoderJni
 * Method:    playStop
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_com_test_recoder_RecoderJni_playStop
        (JNIEnv *, jobject);

/*
 * Class:     com_test_recoder_RecoderJni
 * Method:    record
 * Signature: (Ljava/lang/String;)I
 */
JNIEXPORT jint JNICALL Java_com_test_recoder_RecoderJni_record
        (JNIEnv *, jobject, jstring);

/*
 * Class:     com_test_recoder_RecoderJni
 * Method:    stopRecod
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_com_test_recoder_RecoderJni_stopRecod
        (JNIEnv *, jobject);
/*
 * Class:     com_test_recoder_RecoderJni
 * Method:    setAvailableTime
 * Signature: (Ljava/lang/long)
 */
JNIEXPORT jint JNICALL Java_com_test_recoder_RecoderJniy_setAvailableTime
        (JNIEnv *, jobject, jlong);
/*
 * Class:     com_test_recoder_RecoderJni
 * Method:    getSetTime
 * Signature: ()I
 */
JNIEXPORT jlong JNICALL Java_com_test_recoder_RecoderJni_getSetTime
        (JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif

录音openslRecord.c

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
#include <stdio.h>
#include <android/log.h>
#include <malloc.h>
#include <time.h>

#define LOG_TAG    "opensl-record"
#define LOGI(...)  __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...)  __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
/* Header for class com_test_recoder_RecoderJni */


#ifndef _Included_com_test_recoder_RecoderJni
#define _Included_com_test_recoder_RecoderJni
#ifdef __cplusplus
extern "C" {
#endif

#define NB_BUFFERS_IN_QUEUE 1

/* Explicitly requesting SL_IID_ANDROIDSIMPLEBUFFERQUEUE and SL_IID_ANDROIDCONFIGURATION
 * on the AudioRecorder object */
#define NUM_EXPLICIT_INTERFACES_FOR_RECORDER 2

/* Size of the recording buffer queue */
#define NB_BUFFERS_IN_QUEUE 1
/* Size of each buffer in the queue */
#define BUFFER_SIZE_IN_SAMPLES 8192
#define BUFFER_SIZE_IN_BYTES   (2 * BUFFER_SIZE_IN_SAMPLES)
/* Local storage for Audio data */
int8_t pcmData[NB_BUFFERS_IN_QUEUE * BUFFER_SIZE_IN_BYTES];


static SLObjectItf engineSL = NULL;
SLObjectItf recorder_object;                         //录制对象,这个对象我们从里面获取了2个接口
SLRecordItf recordItf;                        //录制接口
SLAndroidSimpleBufferQueueItf recorder_buffer_queue; //Buffer接口
int8_t *pcm_data; //数据缓存区
static FILE *gFile = NULL;

typedef long long int64;
long long timefromj;

SLEngineItf CreateRecordSL() {
   //1 创建引擎对象
   SLresult re;
   SLEngineItf en; //存引擎接口
   re = slCreateEngine(&engineSL, 0, 0, 0, 0, 0);
   if (re != SL_RESULT_SUCCESS) return NULL;

   //2 实例化 SL_BOOLEAN_FALSE等待对象创建
   re = (*engineSL)->Realize(engineSL, SL_BOOLEAN_FALSE);
   if (re != SL_RESULT_SUCCESS) return NULL;

   //3 获取接口
   re = (*engineSL)->GetInterface(engineSL, SL_IID_ENGINE, &en);
   if (re != SL_RESULT_SUCCESS) return NULL;
   return en;
}

//数据回调函数
void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context) {

   fwrite(pcm_data, BUFFER_SIZE_IN_BYTES, 1, gFile);
   //取完数据,需要调用Enqueue触发下一次数据回调
   (*bq)->Enqueue(bq, pcm_data, BUFFER_SIZE_IN_BYTES);

}



/** 获取当前时间
* @param jniEnv
* @param instance
* @return
*/
int64 getSystemTime(){
    struct timeval tv;   //获取一个时间结构
    gettimeofday(&tv, NULL);   //获取当前时间
    int64 t = tv.tv_sec;
    t *=1000;
    t +=tv.tv_usec/1000;
    return t;
}

/*
* Class:     com_test_recoder_RecoderJni
* Method:    record
* Signature: (Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_com_test_recoder_RecoderJni_record
        (JNIEnv *jniEnv, jobject instance, jstring path) {
    int64 time = getSystemTime();
    LOGE("current_time ,current_time=%lld ", time);
    LOGE("timefromj ,timefromj=%lld ", timefromj);

    if(time > timefromj){ //指定时间
        LOGE("current_time is fail ");
        return -1;
    }
    SLresult re;
    const char *file_path = (*jniEnv)->GetStringUTFChars(jniEnv, path, NULL);
    gFile = fopen(file_path, "w");
    if (!gFile) {
        LOGE("file open failed");
        return -1;
    }
    (*jniEnv)->ReleaseStringUTFChars(jniEnv, path, file_path);

    //设置IO设备(麦克风)
    SLDataLocator_IODevice io_device = {
            SL_DATALOCATOR_IODEVICE,         //类型 这里只能是SL_DATALOCATOR_IODEVICE
            SL_IODEVICE_AUDIOINPUT,          //device类型  选择了音频输入类型
            SL_DEFAULTDEVICEID_AUDIOINPUT,   //deviceID 对应的是SL_DEFAULTDEVICEID_AUDIOINPUT
            NULL                             //device实例
    };
    SLDataSource data_src = {
            &io_device,                      //SLDataLocator_IODevice配置输入
            NULL                             //输入格式,采集的并不需要
    };

    //设置输出buffer队列
    SLDataLocator_AndroidSimpleBufferQueue buffer_queue = {
            SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,    //类型 这里只能是SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE
            2                                           //buffer的数量
    };
    //设置输出数据的格式
    SLDataFormat_PCM format_pcm = {
            SL_DATAFORMAT_PCM,                             //输出PCM格式的数据
            1,                                             //输出的声道数量
            SL_SAMPLINGRATE_44_1,                          //输出的采样频率,这里是44100Hz
            SL_PCMSAMPLEFORMAT_FIXED_16,                   //输出的采样格式,这里是16bit
            SL_PCMSAMPLEFORMAT_FIXED_16,                   //一般来说,跟随上一个参数
            SL_SPEAKER_FRONT_LEFT,  //双声道配置,如果单声道可以用 SL_SPEAKER_FRONT_CENTER
            SL_BYTEORDER_LITTLEENDIAN                      //PCM数据的大小端排列
    };
    SLDataSink audioSink = {
            &buffer_queue,                   //SLDataFormat_PCM配置输出
            &format_pcm                      //输出数据格式
    };



    //1 创建引擎
    SLEngineItf eng = CreateRecordSL();
    if (eng) {
        LOGE("CreateSL success! ");
    } else {
        LOGE("CreateSL failed! ");
    }


//创建录制的对象,并且指定开放SL_IID_ANDROIDSIMPLEBUFFERQUEUE这个接口
    const SLInterfaceID id[1] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE};
    const SLboolean req[1] = {SL_BOOLEAN_TRUE};
    re = (*eng)->CreateAudioRecorder(eng,        //引擎接口
                                     &recorder_object,   //录制对象地址,用于传出对象
                                     &data_src,          //输入配置
                                     &audioSink,         //输出配置
                                     1,                  //支持的接口数量
                                     id,                 //具体的要支持的接口
                                     req                 //具体的要支持的接口是开放的还是关闭的
    );

    if (re != SL_RESULT_SUCCESS) {
        LOGE("CreateAudioRecorder failed!");
        return -1;
    }

    //实例化这个录制对象
    re = (*recorder_object)->Realize(recorder_object, SL_BOOLEAN_FALSE);
    if (re != SL_RESULT_SUCCESS) {
        LOGE("Realize failed!");
    }

    //获取录制接口
    re = (*recorder_object)->GetInterface(recorder_object, SL_IID_RECORD, &recordItf);
    if (re != SL_RESULT_SUCCESS) {
        LOGE("GetInterface1 failed!");
    }
    //获取Buffer接口
    re = (*recorder_object)->GetInterface(recorder_object, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
                                          &recorder_buffer_queue);
    if (re != SL_RESULT_SUCCESS) {
        LOGE("GetInterface2 failed!");
    }

//申请一块内存,注意RECORDER_FRAMES是自定义的一个宏,指的是采集的frame数量,具体还要根据你的采集格式(例如16bit)计算
    pcm_data = malloc(BUFFER_SIZE_IN_BYTES);

//设置数据回调接口bqRecorderCallback,最后一个参数是可以传输自定义的上下文引用
    re = (*recorder_buffer_queue)->RegisterCallback(recorder_buffer_queue, bqRecorderCallback, 0);
    if (re != SL_RESULT_SUCCESS) {
        LOGE("RegisterCallback failed!");
    }
//设置录制器为录制状态 SL_RECORDSTATE_RECORDING
    re = (*recordItf)->SetRecordState(recordItf, SL_RECORDSTATE_RECORDING);
    if (re != SL_RESULT_SUCCESS) {
        LOGE("SetRecordState failed!");
    }
    //在设置完录制状态后一定需要先Enqueue一次,这样的话才会开始采集回调
    re = (*recorder_buffer_queue)->Enqueue(recorder_buffer_queue, pcm_data, 8192);
    if (re != SL_RESULT_SUCCESS) {
        LOGE("Enqueue failed!");
    }
    return 0;
}


/*
 * Class:     com_test_recoder_RecoderJni
 * Method:    stopRecod
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_com_test_recoder_RecoderJni_stopRecod
        (JNIEnv *jniEnv, jobject instance) {
    LOGE("STOP record");
    if (recordItf != NULL) {
        (*recordItf)->SetRecordState(recordItf, SL_RECORDSTATE_STOPPED);
        return 0;
    } else {
        return -1;
    }
}

JNIEXPORT jint JNICALL Java_com_test_recoder_RecoderJni_setAvailableTime
        (JNIEnv *jniEnv, jobject instance, jlong longtime) {
    timefromj  = longtime;
    LOGE("jni receiver time ,send timefromj =%lld ", timefromj);
    return 0;
}

JNIEXPORT jlong JNICALL Java_com_test_recoder_RecoderJni_getSetTime
        (JNIEnv *jniEnv, jobject instance) {
  //  LOGE("jni receiver time ,send timefromj =%lld ", timefromj);
    return timefromj;
}





#ifdef __cplusplus
}
#endif
#endif

openslPlay.c

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#include <stdio.h>
#include <SLES/OpenSLES.h>

#include <android/log.h>
#include <stdio.h>
#include <malloc.h>
#include <SLES/OpenSLES_Android.h>
#include<unistd.h>

#define LOG_TAG    "opensl-play"
#define LOGI(...)  __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...)  __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)

/* Header for class com_test_recoder_MainActivity */
static FILE *File = NULL;
#ifndef _Included_com_test_recoder_RecoderJni
#define _Included_com_test_recoder_RecoderJni
#ifdef __cplusplus
extern "C" {
#endif

static SLObjectItf engineSL = NULL;
static SLPlayItf iplayer = NULL;

SLEngineItf CreateSL() {
    //1 创建引擎对象
    SLresult re;
    SLEngineItf en; //存引擎接口
    re = slCreateEngine(&engineSL, 0, 0, 0, 0, 0);
    if (re != SL_RESULT_SUCCESS) return NULL;

    //2 实例化 SL_BOOLEAN_FALSE等待对象创建
    re = (*engineSL)->Realize(engineSL, SL_BOOLEAN_FALSE);
    if (re != SL_RESULT_SUCCESS) return NULL;

    //3 获取接口
    re = (*engineSL)->GetInterface(engineSL, SL_IID_ENGINE, &en);
    if (re != SL_RESULT_SUCCESS) return NULL;
    return en;
}

//回调函数
void pcmCallBack(SLAndroidSimpleBufferQueueItf bf, void *contex) {
    LOGI("pcmCallBack ");
    static char buf[1024 * 1024] = "";
    if (feof(File) == 0) { //没到结尾
        int len = (int) fread(&buf, 1, 1024, File);
        if (len > 0) {
            // 加入队列
            (*bf)->Enqueue(bf, &buf, len);
        }
    }
}

/*
 * Class:     com_test_recoder_RecoderJni
 * Method:    play
 * Signature: (Ljava/lang/String;)I
 */
JNIEXPORT jint JNICALL Java_com_test_recoder_RecoderJni_play
        (JNIEnv *jniEnv, jobject instance, jstring path) {

    const char *file_path = (*jniEnv)->GetStringUTFChars(jniEnv, path, NULL);
    File = fopen(file_path, "rb");
    if (!File) {
        LOGE("file open failed");
        return -1;
    }
    (*jniEnv)->ReleaseStringUTFChars(jniEnv, path, file_path);

    //1 创建引擎
    SLEngineItf eng = CreateSL();
    if (eng) {
        LOGE("CreateSL success! ");
    } else {
        LOGE("CreateSL failed! ");
        return -1;
    }

    //2 创建混音器
    SLObjectItf mix = NULL;
    SLresult re = 0;
    const SLInterfaceID mids[1] = {SL_IID_ENVIRONMENTALREVERB};
    const SLboolean mreq[1] = {SL_BOOLEAN_FALSE};
    re = (*eng)->CreateOutputMix(eng, &mix, 1, mids, mreq);
    if (re != SL_RESULT_SUCCESS) {
        LOGE("SL_RESULT_SUCCESS failed!");
        return -1;
    }

    re = (*mix)->Realize(mix, SL_BOOLEAN_FALSE);
    if (re != SL_RESULT_SUCCESS) {
        LOGE("(*mix)->Realize failed!");
        return -1;
    }


    SLDataLocator_OutputMix outmix = {SL_DATALOCATOR_OUTPUTMIX, mix};
    SLDataSink audioSink = {&outmix, 0};

    //3 配置音频信息
    //数据定位器 就是定位要播放声音数据的存放位置,分为4种:内存位置,输入/输出设备位置,缓冲区队列位置,和midi缓冲区队列位置。
    SLDataLocator_AndroidSimpleBufferQueue que = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 10};
    //音频格式
    SLDataFormat_PCM pcm = {
            SL_DATAFORMAT_PCM,
            1,//    声道数
            SL_SAMPLINGRATE_44_1,
            SL_PCMSAMPLEFORMAT_FIXED_16,
            SL_PCMSAMPLEFORMAT_FIXED_16,
            SL_SPEAKER_FRONT_RIGHT,
            SL_BYTEORDER_LITTLEENDIAN //字节序,小端
    };
    SLDataSource ds = {&que, &pcm};


    //4 创建播放器
    SLObjectItf player = NULL;
//    SLPlayItf iplayer = NULL;
    SLAndroidSimpleBufferQueueItf pcmQue = NULL;
    const SLInterfaceID ids[] = {SL_IID_BUFFERQUEUE};
    const SLboolean req[] = {SL_BOOLEAN_TRUE};
    re = (*eng)->CreateAudioPlayer(eng, &player, &ds, &audioSink,
                                   sizeof(ids) / sizeof(SLInterfaceID), ids, req);
    if (re != SL_RESULT_SUCCESS) {
        LOGE("CreateAudioPlayer failed!");
    } else {
        LOGE("CreateAudioPlayer success!");
    }
    (*player)->Realize(player, SL_BOOLEAN_FALSE);
    //获取player接口
    re = (*player)->GetInterface(player, SL_IID_PLAY, &iplayer);
    if (re != SL_RESULT_SUCCESS) {
        LOGE("GetInterface SL_IID_PLAY failed!");
    }
    re = (*player)->GetInterface(player, SL_IID_BUFFERQUEUE, &pcmQue);
    if (re != SL_RESULT_SUCCESS) {
        LOGE("GetInterface SL_IID_BUFFERQUEUE failed!");
    }

    //设置回调函数,播放队列空调用
    (*pcmQue)->RegisterCallback(pcmQue, pcmCallBack, 0);

    //设置为播放状态
    (*iplayer)->SetPlayState(iplayer, SL_PLAYSTATE_PLAYING);

    //启动队列回调
    (*pcmQue)->Enqueue(pcmQue, "", 1);
    return 0;
}

/*
 * Class:     com_test_recoder_RecoderJni
 * Method:    playStop
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_com_test_recoder_RecoderJni_playStop
        (JNIEnv *jniEnv, jobject instance) {
    (*iplayer)->SetPlayState(iplayer, SL_PLAYSTATE_STOPPED);
    LOGE("playStop");
    return 0;
}

#ifdef __cplusplus
}
#endif
#endif

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FW_G8Z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值