H.264是比較多開發者使用較多的一種數字視頻壓縮格式,主要用於直播流的傳輸與視頻網站的視頻流傳輸,也有很多開發者開始使用H.265進行視頻壓縮,性能較H.264提高較大。本篇文章着重介紹使用MediaCodec硬件H.264裸字節流數據的實現方式,有關於更多H.264的介紹能夠查看參考文章中H.264的結構介紹。html
MediaCodec類Android提供的用於訪問低層多媒體編/解碼器接口,它是Android低層多媒體架構的一部分,一般與MediaExtractor、MediaMuxer、AudioTrack結合使用,可以編解碼諸如H.26四、H.26五、AAC、3gp等常見的音視頻格式。java
Android 底層多媒體模塊採用的是 OpenMax 框架,任何 Android 底層編解碼模塊的實現,都必須遵循 OpenMax 標準。Google 官方默認提供了一系列的軟件編解碼器:包括:OMX.google.h264.encoder,OMX.google.h264.encoder, OMX.google.aac.encoder, OMX.google.aac.decoder 等等,而硬件編解碼功能,則須要由芯片廠商依照 OpenMax 框架標準來完成,因此,通常採用不一樣芯片型號的手機,硬件編解碼的實現和性能是不一樣的。android
Android 應用層統一由 MediaCodec API 來提供各類音視頻編解碼功能,由參數配置來決定採用何種編解碼算法、是否採用硬件編解碼加速等。git
編解碼器處理輸入數據併產生輸出數據,MediaCodec 使用輸入輸出緩存,異步處理數據。簡要地說,通常的處理步驟以下github
MediaCodec能夠處理具體的視頻流,主要有這幾個方法:算法
初始化MediaCodec數組
/** * 視頻類型 */
private final static String MIME_TYPE = "video/avc";
/** * 初始化播放 */
private void initVideo(SurfaceHolder holder) {
try {
// 初始化MediaCodec,方法有兩種,分別是經過名稱和類型來建立
// 這裏使用經過類型來建立
mMediaCodec = MediaCodec.createDecoderByType(MIME_TYPE);
// 獲取視頻的寬高
mVideoHeight = holder.getSurfaceFrame().width();
mVideoWidth = holder.getSurfaceFrame().height();
// MediaFormat,這個類包含了比特率、幀率、關鍵幀間隔時間等,其中比特率若是過低就會形成相似馬賽克的現象。
mMediaFormat = MediaFormat.createVideoFormat(MIME_TYPE,
1080, 1920);
// 設置比特率
mMediaFormat.setInteger(KEY_BIT_RATE,
mVideoHeight * mVideoWidth * 5);
// 設置幀率
mMediaFormat.setInteger(KEY_FRAME_RATE, 30);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// 描述編碼器要使用的所需比特率模式的鍵
// BITRATE_MODE_CQ: 表示徹底不控制碼率,盡最大可能保證圖像質量
//BITRATE_MODE_CBR: 表示編碼器會盡可能把輸出碼率控制爲設定值
//BITRATE_MODE_VBR: 表示編碼器會根據圖像內容的複雜度(其實是幀間變化量的大小)來動態調整輸出碼率,圖像複雜則碼率高,圖像簡單則碼率低;
mMediaFormat.setInteger(MediaFormat.KEY_BITRATE_MODE,
MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR);
}
mMediaFormat.setInteger(KEY_I_FRAME_INTERVAL, 1);
byte[] headerSps = {0, 0, 0, 1, 103, 66, 0, 41, -115, -115, 64, 80,
30, -48, 15, 8, -124, 83, -128};
byte[] headerPps = {0, 0, 0, 1, 104, -54, 67, -56};
mMediaFormat.setByteBuffer("csd-0", ByteBuffer.wrap(headerSps));
mMediaFormat.setByteBuffer("csd-1", ByteBuffer.wrap(headerPps));
mMediaCodec.configure(mMediaFormat, holder.getSurface(), null, 0);
mMediaCodec.start();
} catch (IOException e) {
e.printStackTrace();
}
}
複製代碼
視頻解碼部分代碼緩存
將接收到或從文件讀取到的byte[]傳入onFrame中架構
/** * 解碼數據並顯示視頻 * buf 視頻數據組 * offset 數據偏移量 * length 有效長度 */
private void onFrame(byte[] buf, int offset, int length) {
try {
ByteBuffer[] inputBuffers = mMediaCodec.getInputBuffers();
int inputBufferIndex = mMediaCodec.dequeueInputBuffer(0);
if (inputBufferIndex >= 0) {
ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
inputBuffer.clear();
inputBuffer.put(buf, offset, length);
mMediaCodec.queueInputBuffer(inputBufferIndex, 0, length, mCount
* 30, 0);
mCount++;
}
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
int outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo, 0);
while (outputBufferIndex >= 0) {
mMediaCodec.releaseOutputBuffer(outputBufferIndex, true);
outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo, 0);
if (!isPlayingSound) {
mHandler.postDelayed(() -> isPlayingSound = true, 1000);
}
}
} catch (Throwable t) {
t.printStackTrace();
}
}
複製代碼
使用與原理能夠瀏覽參考文章3。 具體代碼實現方式可參考此類app