MediaCodec

簡(jiǎn)介

MediaCodec是 Android media 基礎(chǔ)框架的一部分,通常和 MediaExtractor, MediaSync, MediaMuxer, MediaCrypto, MediaDrm, Image, SurfaceAudioTrack 一起使用源梭。

MediaCodec_1.png

這張圖描述了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),如下圖所示:


MediaCodec_4.png

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ù)。


image.png

異步方式的代碼示例如下:

 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ù)切心。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市片吊,隨后出現(xiàn)的幾起案子绽昏,更是在濱河造成了極大的恐慌,老刑警劉巖俏脊,帶你破解...
    沈念sama閱讀 210,914評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件全谤,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡爷贫,警方通過(guò)查閱死者的電腦和手機(jī)认然,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門(mén)补憾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人卷员,你說(shuō)我怎么就攤上這事盈匾。” “怎么了毕骡?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,531評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵削饵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我挺峡,道長(zhǎng)葵孤,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,309評(píng)論 1 282
  • 正文 為了忘掉前任橱赠,我火速辦了婚禮尤仍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘狭姨。我一直安慰自己宰啦,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布饼拍。 她就那樣靜靜地躺著赡模,像睡著了一般。 火紅的嫁衣襯著肌膚如雪师抄。 梳的紋絲不亂的頭發(fā)上漓柑,一...
    開(kāi)封第一講書(shū)人閱讀 49,730評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音叨吮,去河邊找鬼辆布。 笑死,一個(gè)胖子當(dāng)著我的面吹牛茶鉴,可吹牛的內(nèi)容都是我干的锋玲。 我是一名探鬼主播,決...
    沈念sama閱讀 38,882評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼涵叮,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼惭蹂!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起割粮,我...
    開(kāi)封第一講書(shū)人閱讀 37,643評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤盾碗,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后舀瓢,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體置尔,經(jīng)...
    沈念sama閱讀 44,095評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了榜轿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,566評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡朵锣,死狀恐怖谬盐,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情诚些,我是刑警寧澤飞傀,帶...
    沈念sama閱讀 34,253評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站诬烹,受9級(jí)特大地震影響砸烦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜绞吁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評(píng)論 3 312
  • 文/蒙蒙 一幢痘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧家破,春花似錦颜说、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,715評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至烹困,卻和暖如春玄妈,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背髓梅。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,945評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工拟蜻, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人女淑。 一個(gè)月前我還...
    沈念sama閱讀 46,248評(píng)論 2 360
  • 正文 我出身青樓瞭郑,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親鸭你。 傳聞我的和親對(duì)象是個(gè)殘疾皇子屈张,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 前言 MediaCodec前面已經(jīng)做了簡(jiǎn)介,那么這一篇就是使用了袱巨。 參考文章 官方MediaCodec Andro...
    yzzCool閱讀 2,054評(píng)論 0 2
  • 本篇文章是對(duì)官方的文檔MediaCodec[https://developer.android.google.cn...
    leilifengxingmw閱讀 1,795評(píng)論 0 1
  • 前言 MediaCodec大坑絕對(duì)是大坑阁谆,坑的很直溜。本系列是參考 [奇卓社]的文章愉老,喜歡的小伙伴可以直接去看[奇...
    yzzCool閱讀 3,978評(píng)論 0 3
  • 簡(jiǎn)介 從 API 16開(kāi)始场绿,Android提供了MediaCodec類(lèi)以便開(kāi)發(fā)者更加靈活的處理音視頻的編解碼,較M...
    極客匠閱讀 3,000評(píng)論 0 1
  • 推薦指數(shù): 6.0 書(shū)籍主旨關(guān)鍵詞:特權(quán)嫉入、焦點(diǎn)焰盗、注意力璧尸、語(yǔ)言聯(lián)想、情景聯(lián)想 觀點(diǎn): 1.統(tǒng)計(jì)學(xué)現(xiàn)在叫數(shù)據(jù)分析熬拒,社會(huì)...
    Jenaral閱讀 5,704評(píng)論 0 5