Mediacodec
Android從API 16開始提供java層的MediaCodec視頻硬解碼接口叁熔;從API 21权逗,也就是Android 5.0開始提供native層的MediaCodec的接口这刷。
詳細(xì)描述可參見官方文檔:https://developer.android.com/reference/android/media/MediaCodec.html
NDK中附帶的例子使用MediaExtractor
解析視頻文件數(shù)據(jù)蒿褂,其中隱藏了很多細(xì)節(jié)非迹。下面以h264編碼的mp4文件為例毕泌,簡單介紹一下在native層使用MediaCodec對視頻進(jìn)行硬解碼的使用方式喝检。
MediaCodec的接口定義在頭文件media/NdkMediaCodec.h
中挠说,各個(gè)接口參數(shù)的含義不再贅述损俭,僅列出過程和需要注意的細(xì)節(jié)。
1.創(chuàng)建解碼器
const char* mine = "video/avc";
AMediaCodec* mMediaCodec = AMediaCodec_createDecoderByType(mine);
2.配置解碼器
AMediaFormat* videoFormat = AMediaFormat_new();
AMediaFormat_setString(videoFormat, "mime", "video/avc");
AMediaFormat_setInt32(videoFormat, AMEDIAFORMAT_KEY_WIDTH, width); // 視頻寬度
AMediaFormat_setInt32(videoFormat, AMEDIAFORMAT_KEY_HEIGHT, height); // 視頻高度
AMediaFormat_setBuffer(videoFormat, "csd-0", sps, spsSize); // sps
AMediaFormat_setBuffer(videoFormat, "csd-1", pps, ppsSize); // pps
注:我們的視頻文件是用ffmpeg解析的。widht和height可以從ffmpeg中讀取。sps和pps在ffmpeg對應(yīng)的視頻流AVStream->codec->extradata中日裙。
3.向解碼器設(shè)置數(shù)據(jù)
ssize_t bufidx = AMediaCodec_dequeueInputBuffer(mMediaCodec, 2000);
if (bufidx >= 0) {
// 獲取buffer的索引
uint8_t* inputBuf = AMediaCodec_getInputBuffer(mMediaCodec, bufidx, &outsize);
if (inputBuf != nullptr && bufSize <= outsize) {
// 將待解碼的數(shù)據(jù)copy到硬件中
memcpy(inputBuf, bufData, bufSize);
media_status_t status = AMediaCodec_queueInputBuffer(mMediaCodec, bufidx, 0, bufSize, pts, 0);
}
從ffmpeg里面讀取的解碼前的每幀數(shù)據(jù)的前四個(gè)字節(jié)表示該幀數(shù)據(jù)的大小吹艇,而MediaCode要求的數(shù)據(jù)必須以\x00\x00\x00\x01
開頭。將前四個(gè)字節(jié)直接用\x00\x00\x00\x01
替換即可昂拂。從ffmpeg里面讀取的第一幀數(shù)據(jù)可能對應(yīng)多個(gè)h264幀受神,后幾個(gè)h264幀的頭部也要修改,否則前幾幀圖像顯示不正確格侯。
- 以輪詢方式讀取解碼后的數(shù)據(jù)路克,解碼后的數(shù)據(jù)格式在mColorFormat中,本例得到的是NV12格式
AMediaCodecBufferInfo info;
ssize_t outbufidx = AMediaCodec_dequeueOutputBuffer(mMediaCodec, &info, 2000);
if (outbufidx >= 0) {
size_t outsize;
uint8_t* outputBuf = AMediaCodec_getOutputBuffer(mMediaCodec, outbufidx, &outsize);
if (outputBuf != nullptr) {
pts = info.presentationTimeUs;
int32_t pts32 = (int32_t) pts;
uint8_t* dst = buffer;
memcpy(dst, outputBuf, MIN(mOriFrameSize, mFrameSize));
uint8_t * uvBuf = outputBuf + mFrameSize;
uint32_t uvSize = MIN(mOriFrameSize >> 1, mFrameSize >> 1);
uint8_t *uBuf = dst + mOriFrameSize;
uint8_t *vBuf = uBuf + (mOriFrameSize >> 2);
memcpy(uBuf, uvBuf, uvSize);
AMediaCodec_releaseOutputBuffer(mMediaCodec, outbufidx, info.size != 0);
return GOT_OUT_PUT;
}
}
else {
switch (outbufidx) {
case AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED:
// 解碼輸出的格式發(fā)生變化
{
auto format = AMediaCodec_getOutputFormat(mMediaCodec);
AMediaFormat_getInt32(format, "width", &mWidth);
AMediaFormat_getInt32(format, "height", &mHeight);
int32_t localColorFMT;
AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_COLOR_FORMAT, &localColorFMT);
mColorFormat = getTTFormatFromMC(localColorFMT);
int32_t stride = 0;
AMediaFormat_getInt32(format, "stride", &stride);
if(stride == 0) {
stride = mWidth;
}
mLineSize[0] = stride;
mFrameSize = stride * mHeight;
mOriFrameSize = stride * mOriHeight;
return OUTPUT_FORMAT_CHANGED;
}
case AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED:
break;
default:
break;
}
}