網(wǎng)站建站行業(yè)公司主頁(yè)建設(shè)希愛(ài)力副作用太強(qiáng)了
實(shí)現(xiàn)背景
錄像有什么難的?無(wú)非就是數(shù)據(jù)過(guò)來(lái),編碼保存mp4而已,這可能是好多開(kāi)發(fā)者在做錄像模塊的時(shí)候的思考輸出。是的,確實(shí)不難,但是做好,或者和其他模塊有非常好的邏輯配合,確實(shí)不容易。
好多開(kāi)發(fā)者希望聊聊錄像模塊,實(shí)際上錄像這塊,需求層面的東西大家都清楚,無(wú)非就是設(shè)計(jì)的時(shí)候,做的更智能,邏輯清晰而已。
設(shè)計(jì)思路
以大牛直播SDK的錄像模塊的技術(shù)實(shí)現(xiàn)為例,我們?cè)谠O(shè)計(jì)的時(shí)候,確保錄像模塊和RTMP推送、內(nèi)置輕量級(jí)RTSP服務(wù)、轉(zhuǎn)發(fā)模塊、GB28181設(shè)備接入模塊完全隔離,可以組合使用,也可以分開(kāi)始用。
錄像數(shù)據(jù)源,這塊很好理解,無(wú)非就是編碼前的yuv、nv12、nv21、rgb、pcm等( 比如Android camera、camera2,或者otg采集到的數(shù)據(jù)等),編碼成H.264/H.265/AAC,或外部接口直接投遞的編碼后的264、h265、aac等。
錄像模塊的功能層面,比較好理解,比如需要支持隨時(shí)錄像,設(shè)置單個(gè)錄像文件大小、錄像路徑等,并支持純音頻、純視頻、音視頻錄制模式,此外,最好支持錄像過(guò)程中,暫停錄像、恢復(fù)錄像。
從開(kāi)始錄像,到錄像結(jié)束,需要設(shè)計(jì)event callback,告訴上層邏輯,什么時(shí)候開(kāi)始錄像了,什么時(shí)候生成了個(gè)錄像文件,路徑是什么。
- 文件格式:MP4;
- 涉及相關(guān)庫(kù):libSmartPublisher.so
- 頭文件:SmartPublisherJniV2.java
- Jar:smartavengine.jar
接口概述
Android錄像模塊接口概述 | |||
調(diào)用描述 | 接口 | 接口描述 | |
錄像設(shè)置 | 是否錄像 | SmartPublisherSetRecorder | 設(shè)置是否啟用本地錄像 |
創(chuàng)建錄像目錄 | SmartPublisherCreateFileDirectory | 創(chuàng)建錄像文件目錄 | |
設(shè)置錄像目錄 | SmartPublisherSetRecorderDirectory | 設(shè)置錄像文件目錄 | |
音頻錄像 | SmartPublisherSetRecorderAudio | 音頻錄制開(kāi)關(guān) | |
視頻錄像 | SmartPublisherSetRecorderVideo | 視頻錄制開(kāi)關(guān) | |
設(shè)置錄像文件大小 | SmartPublisherSetRecorderFileMaxSize | 設(shè)置每個(gè)錄像文件的大小,比如100M,超過(guò)這個(gè)大小后,會(huì)自動(dòng)生成下一個(gè)錄像文件 | |
開(kāi)始錄像 | SmartPublisherStartRecorder | 開(kāi)始錄像 | |
暫停/恢復(fù)錄像 | SmartPublisherPauseRecorder | Pause recorder(暫停/恢復(fù)錄像) | |
停止錄像 | SmartPublisherStopRecorder | 停止錄像 |
調(diào)用示例
錄像配置
void ConfigRecorderParam() {if (libPublisher != null && publisherHandle != 0) {if (recDir != null && !recDir.isEmpty()) {int ret = libPublisher.SmartPublisherCreateFileDirectory(recDir);if (0 == ret) {if (0 != libPublisher.SmartPublisherSetRecorderDirectory(publisherHandle, recDir)) {Log.e(TAG, "Set record dir failed , path:" + recDir);return;}// 更細(xì)粒度控制錄像的, 一般情況無(wú)需調(diào)用//libPublisher.SmartPublisherSetRecorderAudio(publisherHandle, 0);//libPublisher.SmartPublisherSetRecorderVideo(publisherHandle, 0);if (0 != libPublisher.SmartPublisherSetRecorderFileMaxSize(publisherHandle, 200)) {Log.e(TAG, "SmartPublisherSetRecorderFileMaxSize failed.");return;}} else {Log.e(TAG, "Create record dir failed, path:" + recDir);}}}}
開(kāi)始、停止錄像
class ButtonStartRecorderListener implements View.OnClickListener {public void onClick(View v) {if (isRecording) {stopRecorder();if (!isPushingRtmp && !isRTSPPublisherRunning && !isGB28181StreamRunning) {ConfigControlEnable(true);}btnStartRecorder.setText("實(shí)時(shí)錄像");btnPauseRecorder.setText("暫停錄像");btnPauseRecorder.setEnabled(false);isPauseRecording = true;return;}Log.i(TAG, "onClick start recorder..");if (libPublisher == null)return;if (!isPushingRtmp && !isRTSPPublisherRunning&& !isGB28181StreamRunning) {InitAndSetConfig();}ConfigRecorderParam();int startRet = libPublisher.SmartPublisherStartRecorder(publisherHandle);if (startRet != 0) {if (!isPushingRtmp && !isRTSPPublisherRunning && !isGB28181StreamRunning) {if (publisherHandle != 0) {long handle = publisherHandle;publisherHandle = 0;libPublisher.SmartPublisherClose(handle);}}Log.e(TAG, "Failed to start recorder.");return;}if (!isPushingRtmp && !isRTSPPublisherRunning && !isGB28181StreamRunning) {CheckInitAudioRecorder();ConfigControlEnable(false);}startLayerPostThread();btnStartRecorder.setText("停止錄像");isRecording = true;btnPauseRecorder.setEnabled(true);isPauseRecording = true;}}
停止錄像封裝
//停止錄像private void stopRecorder() {if(!isRecording)return;isRecording = false;if (!isPushingRtmp && !isRTSPPublisherRunning && !isGB28181StreamRunning)stopLayerPostThread();if (!isPushingRtmp && !isRTSPPublisherRunning && !isGB28181StreamRunning) {if (audioRecord_ != null) {Log.i(TAG, "stopRecorder, call audioRecord_.StopRecording..");audioRecord_.Stop();if (audioRecordCallback_ != null) {audioRecord_.RemoveCallback(audioRecordCallback_);audioRecordCallback_ = null;}audioRecord_ = null;}}if (null == libPublisher || 0 == publisherHandle)return;libPublisher.SmartPublisherStopRecorder(publisherHandle);if (!isPushingRtmp && !isRTSPPublisherRunning && !isGB28181StreamRunning) {releasePublisherHandle();}}
暫停/恢復(fù)錄像
class ButtonPauseRecorderListener implements View.OnClickListener {public void onClick(View v) {if (isRecording) {if(isPauseRecording){int ret = libPublisher.SmartPublisherPauseRecorder(publisherHandle, 1);if (ret == 0){isPauseRecording = false;btnPauseRecorder.setText("恢復(fù)錄像");}else if(ret == 3){Log.e(TAG, "Pause recorder failed, please re-try again..");}else{Log.e(TAG, "Pause recorder failed..");}}else{int ret = libPublisher.SmartPublisherPauseRecorder(publisherHandle, 0);if (ret == 0){isPauseRecording = true;btnPauseRecorder.setText("暫停錄像");}else if(ret == 3){Log.e(TAG, "Resume recorder failed, please re-try again..");}else{Log.e(TAG, "Resume recorder failed..");}}}}}
event回調(diào)
case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_RECORDER_START_NEW_FILE:publisher_event = "開(kāi)始一個(gè)新的錄像文件 : " + param3;break;
case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_ONE_RECORDER_FILE_FINISHED:publisher_event = "已生成一個(gè)錄像文件 : " + param3;break;
技術(shù)總結(jié)
錄像模塊,單純地實(shí)現(xiàn)不難,如果是需要和GB28181設(shè)備接入模塊、RTMP推送、輕量級(jí)RTSP服務(wù)模塊一起使用的時(shí)候,需要考慮的就多了,感興趣的開(kāi)發(fā)者,可以酌情參考。