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