1. 程式人生 > 其它 >1對1直播原始碼實現錄音和播放

1對1直播原始碼實現錄音和播放

1.1對1直播原始碼部分開發,實現錄音和播放
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;
}
1對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

以上就是1對1直播原始碼實現錄音和播放, 更多內容歡迎關注之後的文章