簡(jiǎn)介
MediaCodec是 Android media 基礎(chǔ)框架的一部分,通常和 MediaExtractor, MediaSync, MediaMuxer, MediaCrypto, MediaDrm, Image, Surface和AudioTrack 一起使用源梭。
這張圖描述了MediaCodec的整體工作流程刊殉,一般情況下涮坐,由客戶(hù)端向MediaCodec申請(qǐng)一個(gè)空的輸入ByteBuffer吓笙,進(jìn)行數(shù)據(jù)填充己儒,再將ByteBuffer發(fā)送給MediaCodec崎岂,MediaCodec會(huì)采用異步的方式處理這些輸入的數(shù)據(jù),并將處理后的數(shù)據(jù)填充到輸出Buffer中闪湾,消費(fèi)者取到輸出Buffer進(jìn)行消費(fèi)后冲甘,需將緩沖區(qū)釋放,返還給MediaCodec。
數(shù)據(jù)
數(shù)據(jù)載體模式:
ByteBuffer模式:3種數(shù)據(jù)類(lèi)型都可使用ByteBuffer模式江醇。這種模式下濒憋,通常可以使用Image類(lèi)和getInput/OutputImage(int)獲取原始視頻幀陶夜,下面會(huì)具體列舉到凛驮。
Surface模式:當(dāng)處理原始視頻數(shù)據(jù)時(shí),應(yīng)該考慮使用Surface条辟,而不是ByteBuffer來(lái)作為數(shù)據(jù)載體黔夭,這樣可以提高編解碼器性能。因?yàn)橛鸬眨琒urface使用的是更底層的視頻緩沖區(qū)本姥,而不是將數(shù)據(jù)映射或復(fù)制到ByteBuffer中,效率更高杭棵。這種模式下婚惫,可以使用ImageReader類(lèi)來(lái)訪問(wèn)原始視頻幀,并且它仍然比ByteBuffer模式高效颜屠。
數(shù)據(jù)類(lèi)型:
1)壓縮數(shù)據(jù):作為解碼器的輸入數(shù)據(jù)或者編碼器的輸出數(shù)據(jù)
- 指定格式:通過(guò)MediaFormat.KEY_MIME來(lái)指定或獲取格式辰妙,編解碼器才知道如何處理這些數(shù)據(jù)。
- 視頻數(shù)據(jù):一般情況下甫窟,輸入給解碼器或從編碼器得到的一個(gè)ByteBuffer,都會(huì)是完整的一幀數(shù)據(jù)蛙婴。除非設(shè)置了BUFFER_FLAG_PARTIAL_FRAME標(biāo)記粗井,它表示了緩沖區(qū)只包含幀的一部分,解碼器會(huì)對(duì)數(shù)據(jù)進(jìn)行批處理街图,直到?jīng)]有該標(biāo)志的緩沖區(qū)出現(xiàn)浇衬,才開(kāi)始解碼。比如H264解碼餐济,必須將分割符和NALU單元作為一個(gè)完整的數(shù)據(jù)幀耘擂,傳給解碼器才能正確解碼。
- 音頻數(shù)據(jù):音頻的要求則要稍微寬松絮姆,一個(gè)ByteBuffer可能包含多個(gè)編碼的音頻訪問(wèn)單元醉冤。
2) 原始音頻數(shù)據(jù):ByteBuffer包含PCM音頻數(shù)據(jù)的整個(gè)幀,這是每個(gè)聲道按聲道順序的一個(gè)樣本篙悯,每個(gè)PCM音頻樣本都是16位帶符號(hào)整數(shù)或浮點(diǎn)數(shù)蚁阳。格式為AudioFormat.ENCODING_PCM_16BIT才能做處理。
- 解碼器輸出:通過(guò)getOutputFormat()來(lái)獲取MediaFormat
- 編碼器輸入:通過(guò)getInputFormat()來(lái)獲取MediaFormat
獲取音頻采樣數(shù)據(jù)的示例代碼如下:
/**
* 根據(jù)聲道獲取采樣數(shù)據(jù)
*/
short[] getSamplesForChannel(MediaCodec codec, int bufferId, int channelIx) {
//獲取輸出緩沖區(qū)
ByteBuffer outputBuffer = codec.getOutputBuffer(bufferId);
//獲取音頻編碼格式
MediaFormat format = codec.getOutputFormat(bufferId);
//轉(zhuǎn)換字節(jié)順序和Short類(lèi)型
ShortBuffer samples = outputBuffer.order(ByteOrder.nativeOrder()).asShortBuffer();
//獲取聲道數(shù)
int numChannels = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
if (channelIx < 0 || channelIx >= numChannels) {
return null;
}
//獲取存儲(chǔ)采樣數(shù)據(jù)
short[] res = new short[samples.remaining() / numChannels];
for (int i = 0; i < res.length; ++i) {
res[i] = samples.get(i * numChannels + channelIx);
}
return res;
}
3) 原始視頻數(shù)據(jù):在ByteBuffer模式下鸽照,視頻緩沖區(qū)根據(jù)MediaFormat.KEY_COLOR_FORMAT來(lái)進(jìn)行布局螺捐。通過(guò)getCodecInfo().getCapabilitiesForType().colorFormats可以獲取到支持的顏色格式數(shù)組,它包含3種顏色格式:
native raw video format: KEY_COLOR_FORMAT為COLOR_FormatSurface,表示該數(shù)據(jù)將是GraphicBuffer元數(shù)據(jù)的引用定血,在OMX中(即軟編解碼)中赔癌,被稱(chēng)為OMX_COLOR_FormatAndroidOpaque,這類(lèi)格式的數(shù)據(jù)可以作為Surface模式的輸入和輸出澜沟。
flexible YUV buffers:比如CodecCapabilities.COLOR_FormatYUV420Flexible灾票,Surface模式和ByteBuffer模式2中模式,都可以作為輸入和輸出倔喂。
other, specific formats: 除了以上2種铝条,只支持ByteBuffer模式,在MediaCodecInfo.CodecCapabilities中可以看到許多不同的格式席噩,只要后綴為flexible類(lèi)型的班缰,如,CodecCapabilities.COLOR_FormatRGBFlexible悼枢, 都可以使用Image類(lèi)的getInput/OutputImage(int)來(lái)獲取原始視頻幀埠忘。
Build.VERSION_CODES.LOLLIPOP_MR1(5.1)以后,編解碼器都支持YUV420P
生命周期和狀態(tài)
MediaCodec有3種狀態(tài):Stopped馒索,Executing和Released莹妒,其中Stopped和Released又各自細(xì)分成3種子狀態(tài),如下圖所示:
1)Stopped:
- Uninitialized:當(dāng)通過(guò)工廠方法了成功創(chuàng)建編解碼器后绰上,此時(shí)處于Uninitialized子狀態(tài)
- Configured:通過(guò)configure方法配置編解碼器旨怠,此時(shí)處于Configured子狀態(tài),接著蜈块,需要調(diào)用start方法來(lái)啟動(dòng)鉴腻,讓編解碼器進(jìn)入Executing狀態(tài)的Flushed子狀態(tài),才能輸入數(shù)據(jù)給編解碼器處理
- Error:
2)Executing:
- Flushed:執(zhí)行start方法后百揭,此時(shí)處于Flushed子狀態(tài)
- Running :當(dāng)?shù)谝粋€(gè)輸入緩沖區(qū)被出隊(duì)爽哎,編解碼器便進(jìn)入Running子狀態(tài),這意味著器一,大部分時(shí)間編解碼器都處于此狀態(tài)
- End-of-Stream:當(dāng)給編解碼器發(fā)送一個(gè)帶有End-of-Stream標(biāo)記的Buffer后课锌,編解碼器就切換為End-of-Stream子狀態(tài),此時(shí)祈秕,編解碼器不再接收輸入數(shù)據(jù)渺贤,但仍舊會(huì)繼續(xù)輸出,直到end-of-stream標(biāo)記輸出
3)Released:
- Released:Stopped和Executing都可切換至此狀態(tài)踢步,當(dāng)使用編碼器操作完成后癣亚,應(yīng)該調(diào)用release方法,使編解碼器進(jìn)入此狀態(tài)
狀態(tài)重置:
- flush:在Executing狀態(tài)下获印,調(diào)用flush方法述雾,來(lái)使編解碼器回到Flushed子狀態(tài)
- stop:在Executing狀態(tài)下街州,調(diào)用stop方法,使編解碼器進(jìn)入U(xiǎn)ninitialized子狀態(tài)玻孟,此時(shí)可以調(diào)用configure方法來(lái)重新配置唆缴,進(jìn)入下一輪循環(huán)
- reset:在某些情況下,編解碼器會(huì)出現(xiàn)異常黍翎,此時(shí)應(yīng)該使用reset而不是stop方法面徽,使編解碼進(jìn)入U(xiǎn)ninitialized狀態(tài),事實(shí)上匣掸,reset可以在任何時(shí)候被調(diào)用趟紊,如果異常發(fā)生后,不準(zhǔn)備重新使用編解碼器碰酝,那應(yīng)該調(diào)用release進(jìn)行釋放
創(chuàng)建
通過(guò)編解碼器類(lèi)型和編解碼器名字2種方式霎匈,可以創(chuàng)建編解碼器,它們分別對(duì)應(yīng)以下工廠方法:
//根據(jù)編碼器類(lèi)型創(chuàng)建解碼器
public static MediaCodec createEncoderByType(@NonNull String type)
//根據(jù)解碼器類(lèi)型創(chuàng)建解碼器
public static MediaCodec createDecoderByType(@NonNull String type)
//根據(jù)編解碼器名字創(chuàng)建編解碼器
public static MediaCodec createByCodecName(@NonNull String name)
MediaFormat包含了許多類(lèi)型送爸,如下:
public final class MediaFormat {
铛嘱。。袭厂。墨吓。。
//視頻類(lèi)型
public static final String MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8";
public static final String MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9";
public static final String MIMETYPE_VIDEO_AV1 = "video/av01";
public static final String MIMETYPE_VIDEO_AVC = "video/avc";
public static final String MIMETYPE_VIDEO_HEVC = "video/hevc";
//音頻類(lèi)型
public static final String MIMETYPE_AUDIO_AMR_NB = "audio/3gpp";
public static final String MIMETYPE_AUDIO_AMR_WB = "audio/amr-wb";
public static final String MIMETYPE_AUDIO_MPEG = "audio/mpeg";
public static final String MIMETYPE_AUDIO_AAC = "audio/mp4a-latm";
public static final String MIMETYPE_AUDIO_QCELP = "audio/qcelp";
public static final String MIMETYPE_AUDIO_VORBIS = "audio/vorbis";
public static final String MIMETYPE_AUDIO_OPUS = "audio/opus";
纹磺。帖烘。。橄杨。蚓让。
}
在創(chuàng)建解碼器時(shí),如果是本地文件或者網(wǎng)絡(luò)流讥珍,可以配合使用MediaExtractor解封裝器來(lái)做格式提取,如下:
//mTrackIndex為視頻軌索引獲取格式
MediaFormat decoderFormat = mediaExtractor.getTrackFormat(mTrackIndex);
//創(chuàng)建解碼器
MediaCodec decoder = MediaCodec.createDecoderByType(decoderFormat.getString(MediaFormat.KEY_MIME));
在創(chuàng)建編碼器時(shí)窄瘟,可以直接指定期望的編碼類(lèi)型衷佃,如下:
//指定格式
MediaFormat encoderFormat = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, width, height);
//創(chuàng)建編碼器
MediaCodec encoder = MediaCodec.createEncoderByType(encoderFormat.getString(MediaFormat.KEY_MIME));
以上是硬編解碼器的創(chuàng)建,如果創(chuàng)建失敗蹄葱,可以通過(guò)指定軟解碼器名字來(lái)創(chuàng)建氏义,如下:
//創(chuàng)建軟解碼器
MediaCodec decoder = MediaCodec.createByCodecName("OMX.google.h264.decoder");
//創(chuàng)建軟編碼器
MediaCodec encoder = MediaCodec.createByCodecName("OMX.google.h264.encoder");
通常,以"OMX.google."為前綴的名字图云,即為軟編解碼類(lèi)型
初始化配置
void configure (MediaFormat format, Surface surface, MediaCrypto crypto, int flags)
void configure (MediaFormat format, Surface surface, int flags, MediaDescrambler descrambler)
MediaCrypto和MediaDescrambler:用于加解密處理惯悠,都是可空參數(shù),如果不涉及加密和解密竣况,它們沒(méi)有區(qū)別克婶,會(huì)執(zhí)行同一個(gè)重載方法
flags:傳入MediaCodec.CONFIGURE_FLAG_ENCODE時(shí)(它的值為1),用來(lái)指定創(chuàng)建編碼器,奇怪的是MediaCodec沒(méi)有定義一個(gè)類(lèi)似MediaCodec.CONFIGURE_FLAG_DECODE的常量用于創(chuàng)建解碼器情萤,這里通常直接傳入0來(lái)指定創(chuàng)建解碼器
Surface:調(diào)用releaseOutputBuffer(int index, boolean render)方法將render設(shè)置為true鸭蛙,解碼器會(huì)將輸出數(shù)據(jù)渲染到此處指定的Surface,當(dāng)Surface不再使用或顯示時(shí)筋岛,緩沖區(qū)會(huì)自動(dòng)釋放給編解碼器
以下為編解碼配置的示例代碼:
//解碼器配置
decoder.configure(decoderFormat, new Surface(new SurfaceTexture(getTextureId())), null, 0);
decoder.start()
這里指定了解碼器最終輸出渲染的表面娶视,用來(lái)輸出到開(kāi)辟好的紋理空間
//編碼器配置
encoder.configure(encoderFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
Surface surface = encoder.createInputSurface()
encoder.start()
編碼器創(chuàng)建的Surface只能用于硬件加速api的渲染輸入,如OpenGL睁宰,這就是Surface模式的用法肪获,比如編碼器創(chuàng)建了自己的Surface,可以用來(lái)關(guān)聯(lián)EGL做為編碼器的數(shù)據(jù)輸入柒傻,替代ByteBuffer輸入方式孝赫。
還有另一種方式,也可以使用createPersistentInputSurface ()來(lái)創(chuàng)建Surface诅愚,其他的編碼器隨后可以調(diào)用setInputSurface(Surface)方法來(lái)繼續(xù)使用這個(gè)Surface寒锚,但同一時(shí)間內(nèi),只能一個(gè)編解碼器使用违孝。
createInputSurface和setInputSurface方法必須在configure方法之后刹前,start方法之前調(diào)用,否則拋IllegalStateException異常雌桑。
持久化表面必須使用createPersistentInputSurface創(chuàng)建喇喉,否則拋IllegalArgumentException異常。
特殊數(shù)據(jù)
某些格式如校坑,AAC和MPEG4拣技,H.264和H.265要求幀數(shù)據(jù)的前綴,包含設(shè)置數(shù)據(jù)或編解碼器特定數(shù)據(jù)的緩沖區(qū)耍目,如sps和pps膏斤。處理此類(lèi)壓縮格式時(shí),必須在start方法之后且任何幀數(shù)據(jù)之前邪驮,將這些數(shù)據(jù)輸送給編解碼器莫辨。這類(lèi)數(shù)據(jù)用BUFFER_FLAG_CODEC_CONFIG來(lái)做標(biāo)記。
通常不使用ByteBuffer來(lái)直接提交毅访,而是在configure方法時(shí)通過(guò)MediaFormat進(jìn)行設(shè)置沮榜,它在start方法調(diào)用后,會(huì)直接提交給編解碼器喻粹。編解碼器同樣會(huì)輸出到輸出緩沖區(qū)中蟆融,因此,在進(jìn)行編碼往Muxer寫(xiě)入數(shù)據(jù)時(shí)守呜,攜帶BUFFER_FLAG_CODEC_CONFIG標(biāo)記的數(shù)據(jù)不用再次寫(xiě)入型酥,它應(yīng)該通過(guò)MediaFormat傳遞給Muxer山憨。
數(shù)據(jù)處理
1)同步方式:
系統(tǒng)版本5.0之前只能使用同步方式來(lái)處理,整個(gè)過(guò)程如開(kāi)頭的流程圖所示冕末,下面以解碼流程為例:
/**
* 解封裝
*/
private int drainExtractor(long timeoutUs) {
if (mIsExtractorEOS) return DRAIN_STATE_NONE;
int trackIndex = mExtractor.getSampleTrackIndex();
if (trackIndex >= 0 && trackIndex != mTrackIndex) {
return DRAIN_STATE_NONE;
}
//步驟1
int result = mDecoder.dequeueInputBuffer(timeoutUs);
if (result < 0) return DRAIN_STATE_NONE;
if (trackIndex < 0) {
mIsExtractorEOS = true;
mDecoder.queueInputBuffer(result, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
return DRAIN_STATE_NONE;
}
//步驟2
ByteBuffer buffer = mDecoder.getInputBuffer(result);
int sampleSize = mExtractor.readSampleData(buffer, 0);
boolean isKeyFrame = (mExtractor.getSampleFlags() & MediaExtractor.SAMPLE_FLAG_SYNC) != 0;
//步驟3
mDecoder.queueInputBuffer(result, 0, sampleSize, mExtractor.getSampleTime(), isKeyFrame ? MediaCodec.BUFFER_FLAG_KEY_FRAME : 0);
mExtractor.advance();
return DRAIN_STATE_CONSUMED;
}
步驟1:從解碼器獲取輸入緩沖區(qū)的id
- timeoutUs == 0萍歉,立即返回
- timeoutUs < 0,無(wú)限期等待
- timeoutUs > 0档桃,等待“timeoutUs”微秒超時(shí)
步驟2:根據(jù)緩沖id獲取輸入緩沖區(qū)
步驟3:將充滿數(shù)據(jù)的輸入緩沖求傳遞給解碼器枪孩,最后的flags標(biāo)記有幾種類(lèi)型
- BUFFER_FLAG_KEY_FRAME:是否關(guān)鍵幀
- BUFFER_FLAG_CODEC_CONFIG:是否sps和pps等特殊數(shù)據(jù),通常應(yīng)該用MediaFormat做傳遞
- BUFFER_FLAG_PARTIAL_FRAME:通常一個(gè)ByteBuffer包含一幀完整視頻數(shù)據(jù)藻肄,除非指定該標(biāo)志蔑舞,出現(xiàn)該標(biāo)記解碼器會(huì)批量處理多個(gè)緩沖區(qū),直到?jīng)]有該標(biāo)記出現(xiàn)嘹屯,才進(jìn)行解碼攻询,大多數(shù)情況下不會(huì)使用
- BUFFER_FLAG_END_OF_STREAM:結(jié)束輸入數(shù)據(jù)給解碼器,除非調(diào)用flush方法州弟,否則不要再向編解碼器輸入緩沖區(qū)钧栖,可以在最后一個(gè)帶有有效數(shù)據(jù)的緩沖區(qū)上加上此標(biāo)記,也可以用一個(gè)空的緩沖區(qū)
來(lái)傳遞婆翔,此時(shí)的pts可為0
下面從解碼器獲取輸出數(shù)據(jù):
private int drainDecoder(long timeoutUs) throws InterruptedException {
if (mIsDecoderEOS) return DRAIN_STATE_NONE;
//1
int result = mDecoder.dequeueOutputBuffer(mBufferInfo, timeoutUs);
switch (result) {
case MediaCodec.INFO_TRY_AGAIN_LATER:
return DRAIN_STATE_NONE;
case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
return DRAIN_STATE_SHOULD_RETRY_IMMEDIATELY;
}
//2
if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
mEncoder.signalEndOfInputStream();
mIsDecoderEOS = true;
mBufferInfo.size = 0;
}
//3
boolean doRender = (mBufferInfo.size > 0);
mDecoder.releaseOutputBuffer(result, doRender);
......
return DRAIN_STATE_CONSUMED;
}
步驟1:從解碼器獲取輸出緩沖區(qū)和緩沖區(qū)信息拯杠,result返回緩沖區(qū)索引或常量值
- INFO_TRY_AGAIN_LATER:獲取超時(shí)
- INFO_OUTPUT_BUFFERS_CHANGED:表明輸出緩沖區(qū)數(shù)據(jù)已更改,在5.0前啃奴,此時(shí)用getOutputBuffers方法獲取緩沖區(qū)數(shù)組潭陪,5.0后,此方式已過(guò)期最蕾,可以忽略
- INFO_OUTPUT_FORMAT_CHANGED:表明輸出格式已更改依溯,后續(xù)數(shù)據(jù)將采用新的格式,此時(shí)可以通過(guò)getOutputFormat方法來(lái)獲取新的格式瘟则,隨后如果不出現(xiàn)超時(shí)黎炉,通常開(kāi)始返回正確的輸出緩沖區(qū)索引
步驟2:接受到帶有BUFFER_FLAG_END_OF_STREAM標(biāo)記的緩沖區(qū),表明所有解碼數(shù)據(jù)已輸出完成醋拧,不會(huì)再有輸出拜隧,signalEndOfInputStream方法通知編碼器輸入結(jié)束
步驟3:使用完輸出緩沖區(qū)后,調(diào)用releaseOutputBuffer釋放回給解碼器趁仙,doRender為true,則緩沖區(qū)的數(shù)據(jù)會(huì)渲染到在configure方法中配置的Surface
可以不立即queueinputbuffer/releaseOutputBuffer到編解碼器垦页,但持有input/outputbuffer可能會(huì)使編解碼器停止工作雀费,并且此行為取決于設(shè)備。 編解碼器有可能在產(chǎn)生輸出緩沖區(qū)之前暫停痊焊,直到所有未完成的緩沖區(qū)queueinputbuffer/releaseOutputBuffer盏袄。 因此忿峻,用戶(hù)最好每次獲得緩沖區(qū)后執(zhí)行釋放操作。
2)異步方式:
在系統(tǒng)版本5.0及以上增加了異步處理方式辕羽,同步方式仍可以使用逛尚,但官方推薦首選異步方式,它的流程狀態(tài)和同步方式稍微有些不同刁愿,再調(diào)用start方法后绰寞,狀態(tài)自動(dòng)切換為Running子狀態(tài),并且在調(diào)用flush方法后铣口,必須再次調(diào)用start使?fàn)顟B(tài)流轉(zhuǎn)為Running狀態(tài)滤钱,才能開(kāi)始輸入數(shù)據(jù)。
異步方式的代碼示例如下:
MediaCodec codec = MediaCodec.createByCodecName(name);
MediaFormat mOutputFormat; // member variable
codec.setCallback(new MediaCodec.Callback() {
@Override
void onInputBufferAvailable(MediaCodec mc, int inputBufferId) {
ByteBuffer inputBuffer = codec.getInputBuffer(inputBufferId);
// fill inputBuffer with valid data
…
codec.queueInputBuffer(inputBufferId, …);
}
@Override
void onOutputBufferAvailable(MediaCodec mc, int outputBufferId, …) {
ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId);
MediaFormat bufferFormat = codec.getOutputFormat(outputBufferId); // option A
// bufferFormat is equivalent to mOutputFormat
// outputBuffer is ready to be processed or rendered.
…
codec.releaseOutputBuffer(outputBufferId, …);
}
@Override
void onOutputFormatChanged(MediaCodec mc, MediaFormat format) {
// Subsequent data will conform to new format.
// Can ignore if using getOutputFormat(outputBufferId)
mOutputFormat = format; // option B
}
@Override
void onError(…) {
…
}
});
codec.configure(format, …);
mOutputFormat = codec.getOutputFormat(); // option B
codec.start();
// wait for processing to complete
codec.stop();
codec.release();
setCallback方法必須在configure方法之前被調(diào)用脑题,并且不應(yīng)該再使用getInputBuffers件缸,getOutputBuffers,dequeueInputBuffer和dequeueOutputBuffer方法叔遂。
最后
當(dāng)編解碼器使用Surface模式作為數(shù)據(jù)輸入源或輸出源時(shí)他炊,對(duì)應(yīng)的緩沖區(qū)無(wú)法訪問(wèn):
輸出源:使用輸出Surface時(shí),數(shù)據(jù)處理幾乎與ByteBuffer模式相同已艰。但是痊末,輸出緩沖區(qū)將不可訪問(wèn),并表示為空值旗芬。例如舌胶,getOutputBuffer / Image(int)將返回null,getOutputBuffers將返回僅包含null的數(shù)組疮丛。
輸入源:使用輸入Surface時(shí)幔嫂,沒(méi)有可訪問(wèn)的輸入緩沖區(qū),因?yàn)榫彌_區(qū)會(huì)自動(dòng)從輸入表面?zhèn)鬟f到編解碼器誊薄。調(diào)用dequeueInputBuffer會(huì)拋出IllegalStateException履恩,并且getInputBuffers返回一個(gè)不可寫(xiě)入的偽造ByteBuffer數(shù)組。調(diào)用signalEndOfInputStream以信號(hào)流結(jié)束后呢蔫,輸入表面將立即停止向編解碼器提交數(shù)據(jù)切心。