1對1直播原始碼實現錄音和播放
阿新 • • 發佈:2021-08-16
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(); publicnative 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直播原始碼實現錄音和播放, 更多內容歡迎關注之後的文章