溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Android端的短視頻開發(fā),我們該如何快速實現(xiàn)移動端短視頻功能?

發(fā)布時間:2020-07-24 03:12:08 來源:網(wǎng)絡(luò) 閱讀:1470 作者:十年開發(fā) 欄目:移動開發(fā)

當(dāng)下抖音非?;馃?,是不是也很心動做一個類似的app嗎?

一.短視頻內(nèi)容生產(chǎn)

優(yōu)質(zhì)短視頻內(nèi)容的產(chǎn)生依賴于短視頻的采集和特效編輯,這就要求在進(jìn)行抖音APP開發(fā)時,用到基礎(chǔ)的美顏、混音、濾鏡、變速、圖片視頻混剪、字幕等功能,在這些功能基礎(chǔ)上,進(jìn)行預(yù)處理,結(jié)合OpenGL、AI、AR技術(shù),產(chǎn)生很多有趣的動態(tài)貼紙玩法,使得短視頻內(nèi)容更具創(chuàng)意。

Android端的短視頻開發(fā),我們該如何快速實現(xiàn)移動端短視頻功能?

視頻錄制的大致實現(xiàn)流程是先由 Camera 、 AudioRecord 進(jìn)行最原始的相機(jī)畫面以及聲音的采集,然后將采集的數(shù)據(jù)進(jìn)行濾鏡、降噪等前處理,處理完成后由 MediaCodec 進(jìn)行硬件編碼,最后采用 MediaMuxer 生成最終的 MP4 文件。

二.短視頻處理播放

視頻的處理和播放主要是視頻的清晰度、觀看流暢度方面的體驗。在這方面來講,可以采用“窄帶高清”技術(shù),在節(jié)省碼率的同時能夠提供更加清晰的觀看體驗,經(jīng)過測試,同等視頻質(zhì)量下最高可以節(jié)省20-40%帶寬。除了帶寬之外,短視頻內(nèi)容的存儲和CDN優(yōu)化也尤為重要,通常我們需要上傳到云存儲服務(wù)器的內(nèi)容是短視頻內(nèi)容和封面內(nèi)容。

而CDN優(yōu)化帶給短視頻平臺的則是進(jìn)一步的短視頻首次載入和循環(huán)播放方面的體驗。比如針對首播慢的問題,像阿里云播放器支持QUIC協(xié)議,基于CDN的調(diào)度,可以使短視頻首次播放秒開的成功率達(dá)到98%,此外在循環(huán)播放時還可以邊播放邊緩存,用戶反復(fù)觀看某一短視頻時就不用耗費流量了。

三.錄制視頻的方式

在Android系統(tǒng)當(dāng)中,如果需要一臺Android設(shè)備來獲取到一個MP4這樣的視頻文件的話,主流的方式一共與三種:MediaRecorder、MediaCodec+MediaMuxer、FFmpeg。

MediaRecorder:是Android系統(tǒng)直接提供給我們的錄制類,用于錄制音頻和視頻的一個類,簡單方便,不需要理會中間錄制過程,結(jié)束錄制后可以直接得到音頻文件進(jìn)行播放,錄制的音頻文件是經(jīng)過壓縮的,需要設(shè)置編碼器,錄制的音頻文件可以用系統(tǒng)自帶的播放器播放。

優(yōu)點:大部分以及集成,直接調(diào)用相關(guān)接口即可,代碼量小,簡單穩(wěn)定;

缺點:無法實時處理音頻;輸出的音頻格式不是很多。

MediaCodec+MediaMuxer: MediaCodec 與 MediaMuxer結(jié)合使用同樣能夠?qū)崿F(xiàn)錄制的功能。MediaCodec是Android提供的編解碼類,MediaMuxer則是復(fù)用類(生成視頻文件)。從易用性的角度上來說肯定不如MediaRecorder,但是允許我們進(jìn)行更加靈活的操作,比如需要給錄制的視頻添加水印等各種效果。

優(yōu)點: 與MediaRecorder一樣低功耗速度快,并且更加靈活

缺點: 支持的格式有限,兼容性問題

FFmpeg:?FFmpeg(Fast forword mpeg,音視頻轉(zhuǎn)換器)是一個開源免費跨平臺的視頻和音頻流方案,它提供了錄制/音視頻編解碼、轉(zhuǎn)換以及流化音視頻的完整解決方案。主要的作用在于對多媒體數(shù)據(jù)進(jìn)行解協(xié)議、解封裝、解碼以及轉(zhuǎn)碼等操作

優(yōu)點:格式支持非常的強(qiáng),十分的靈活,功能強(qiáng)大,兼容性好;

缺點:C語言些的音視頻編解碼程序,使用起來不是很方便。

Android端的短視頻開發(fā),我們該如何快速實現(xiàn)移動端短視頻功能?

雖然從數(shù)據(jù)看來FFmpeg是最好的,但是我們得首先排除這種,因為他的易用性是最差的;其次,MediaRecorder也是需要排除的,所以在這里我比較推薦MediaCodec+MediaMuxer這種方式。

四.編碼器參數(shù)

碼率:數(shù)據(jù)傳輸時單位時間傳送的數(shù)據(jù)位數(shù),kbps:千位每秒。碼率和質(zhì)量成正比,也和文件體積成正比。碼率超過一定數(shù)值,對圖像的質(zhì)量沒有多大的影響。

幀數(shù):每秒顯示多少個畫面,fps

關(guān)鍵幀間隔:在H.264編碼中,編碼后輸出的壓縮圖像數(shù)據(jù)有多種,可以簡單的分為關(guān)鍵幀和非關(guān)鍵幀。關(guān)鍵幀能夠進(jìn)行獨立解碼,看成是一個圖像經(jīng)過壓縮的產(chǎn)物。而非關(guān)鍵幀包含了與其他幀的“差異”信息,也可以稱呼為“參考幀”,它的解碼需要參考關(guān)鍵幀才能夠解碼出一個圖像。非關(guān)鍵幀擁有更高的壓縮率。

五、MediaCodec+MediaMuxer的使用

MediaMuxer和MediaCodec這兩個類,它們的參考文http://developer.android.com/reference/android/media/MediaMuxer.html和http://developer.android.com/reference/android/media/MediaCodec.html,里邊有使用的框架。這個組合可以實現(xiàn)很多功能,比如音視頻文件的編輯(結(jié)合MediaExtractor),用OpenGL繪制Surface并生成mp4文件,屏幕錄像以及類似Camera app里的錄像功能(雖然這個用MediaRecorder更合適)等。

它們一個是生成視頻,一個生成音頻,這里把它們結(jié)合一下,同時生成音頻和視頻?;究蚣芎土鞒倘缦拢?/p>

Android端的短視頻開發(fā),我們該如何快速實現(xiàn)移動端短視頻功能?

首先是錄音線程,主要參考HWEncoderExperiments。通過AudioRecord類接收來自麥克風(fēng)的采樣數(shù)據(jù),然后丟給Encoder準(zhǔn)備編碼:

AudioRecord audio_recorder; 
audio_recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, 
 SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT, buffer_size); 
// ... 
audio_recorder.startRecording(); 
while (is_recording) { 
 byte[] this_buffer = new byte[frame_buffer_size]; 
 read_result = audio_recorder.read(this_buffer, 0, frame_buffer_size); // read audio raw data 
 // … 
 presentationTimeStamp = System.nanoTime() / 1000; 
 audioEncoder.offerAudioEncoder(this_buffer.clone(), presentationTimeStamp); // feed to audio encoder 

} 

這里也可以設(shè)置AudioRecord的回調(diào)(通過setRecordPositionUpdateListener())來觸發(fā)音頻數(shù)據(jù)的讀取。offerAudioEncoder()里主要是把a(bǔ)udio采樣數(shù)據(jù)送入音頻MediaCodec的InputBuffer進(jìn)行編碼:

ByteBuffer[] inputBuffers = mAudioEncoder.getInputBuffers(); 
int inputBufferIndex = mAudioEncoder.dequeueInputBuffer(-1); 
if (inputBufferIndex >= 0) { 
 ByteBuffer inputBuffer = inputBuffers[inputBufferIndex]; 
 inputBuffer.clear(); 
 inputBuffer.put(this_buffer); 
 ... 
 mAudioEncoder.queueInputBuffer(inputBufferIndex, 0, this_buffer.length, presentationTimeStamp, 0); 
} 

下面,參考Grafika-SoftInputSurfaceActivity,并加入音頻處理。主循環(huán)大體分四部分:

try { 
 // Part 1 
 prepareEncoder(outputFile); 
 ... 
 // Part 2 
 for (int i = 0; i < NUM_FRAMES; i++) { 
 generateFrame(i); 
 drainVideoEncoder(false); 
 drainAudioEncoder(false); 
 } 
 // Part 3 
 ... 
 drainVideoEncoder(true); 
 drainAudioEncoder(true); 
} catch (IOException ioe) { 
 throw new RuntimeException(ioe); 
} finally { 
 // Part 4 
 releaseEncoder(); 
} 

第1部分是準(zhǔn)備工作,除了video的MediaCodec,這里還初始化了audio的MediaCodec:

MediaFormat audioFormat = new MediaFormat(); 
audioFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, 44100); 
audioFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1); 
... 
mAudioEncoder = MediaCodec.createEncoderByType(AUDIO_MIME_TYPE); 
mAudioEncoder.configure(audioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); 
mAudioEncoder.start(); 

第2部分進(jìn)入主循環(huán),app在Surface上直接繪圖,由于這個Surface是從MediaCodec中用createInputSurface()申請來的,所以畫完后不用顯式用queueInputBuffer()交給Encoder。drainVideoEncoder()和drainAudioEncoder()分別將編碼好的音視頻從buffer中拿出來(通過dequeueOutputBuffer()),然后交由MediaMuxer進(jìn)行混合(通過writeSampleData())。注意音視頻通過PTS(Presentation time stamp,決定了某一幀的音視頻數(shù)據(jù)何時顯示或播放)來同步,音頻的time stamp需在AudioRecord從MIC采集到數(shù)據(jù)時獲取并放到相應(yīng)的bufferInfo中,視頻由于是在Surface上畫,因此直接用dequeueOutputBuffer()出來的bufferInfo中的就行,最后將編碼好的數(shù)據(jù)送去MediaMuxer進(jìn)行多路混合。

注意這里Muxer要等把a(bǔ)udio track和video track都加入了再開始。MediaCodec在一開始調(diào)用dequeueOutputBuffer()時會返回一次INFO_OUTPUT_FORMAT_CHANGED消息。我們只需在這里獲取該MediaCodec的format,并注冊到MediaMuxer里。接著判斷當(dāng)前audio track和video track是否都已就緒,如果是的話就啟動Muxer。

總結(jié)來說,drainVideoEncoder()的主邏輯大致如下,drainAudioEncoder也是類似的,只是把video的MediaCodec換成audio的MediaCodec即可。

while(true) { 
 int encoderStatus = mVideoEncoder.dequeueOutputBuffer(mBufferInfo, TIMEOUT_USEC); 
 if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) { 
 ... 
 } else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { 
 encoderOutputBuffers = mVideoEncoder.getOutputBuffers(); 
 } else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 
 MediaFormat newFormat = mAudioEncoder.getOutputFormat(); 
 mAudioTrackIndex = mMuxer.addTrack(newFormat); 
 mNumTracksAdded++; 
 if (mNumTracksAdded == TOTAL_NUM_TRACKS) { 
 mMuxer.start(); 
 } 
 } else if (encoderStatus < 0) { 
 ... 
 } else { 
 ByteBuffer encodedData = encoderOutputBuffers[encoderStatus]; 
 ... 
 if (mBufferInfo.size != 0) { 
 mMuxer.writeSampleData(mVideoTrackIndex, encodedData, mBufferInfo); 
 } 
 mVideoEncoder.releaseOutputBuffer(encoderStatus, false); 
 if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { 
 break; 
 } 
 } 

} 

第3部分是結(jié)束錄制,發(fā)送EOS信息,這樣在drainVideoEncoder()和drainAudioEncoder中就可以根據(jù)EOS退出內(nèi)循環(huán)。第4部分為清理工作。把a(bǔ)udio和video的MediaCodec,MediaCodec用的Surface及MediaMuxer對象釋放。

最后幾點注意:

1. 在AndroidManifest.xml里加上錄音權(quán)限,否則創(chuàng)建AudioRecord對象時鐵定失?。?/p>

<uses-permission android:name="android.permission.RECORD_AUDIO"/>

2. 音視頻通過PTS同步,兩個的單位要一致。

3. MediaMuxer的使用要按照Constructor -> addTrack -> start -> writeSampleData -> stop 的順序。如果既有音頻又有視頻,在stop前兩個都要writeSampleData()過。

總結(jié)

以上就是抖音類APP的部分內(nèi)容,其中的步驟和過程是我親自實踐過的,按照上述的過程應(yīng)該都可以正常運行,寫這一篇文章花了很多時間,希望所有看了這篇文章的朋友們都能夠有一定的收獲。此外更多的Android短視頻詳細(xì)內(nèi)容可見下方附帶資料:

【附】相關(guān)視頻及資料

鏈接:https://pan.baidu.com/s/17TwdsQizp1R02zKHoykTMA
提取碼:krzw

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI