MediaCodec專(zhuān)題(二):使用

前言

MediaCodec前面已經(jīng)做了簡(jiǎn)介卸留,那么這一篇就是使用了父晶。

參考文章

  1. 官方MediaCodec
  2. Android視頻處理之MediaCodec-2-使用
  3. 安卓解碼器MediaCodec解析

Creation 創(chuàng)建

1陡舅、createByCodecName(String)

使用MediaCodecList為特定的MediaFormat創(chuàng)建MediaCodec

  1. 解碼文件或流時(shí)仪糖,可以從MediaExtractor#getTrackFormat獲得所需的格式挂脑。
  2. 使用MediaFormat#setFeatureEnabled注入要添加的所有特定功能,然后調(diào)用MediaCodecList#findDecoderForFormat以獲取可以處理該特定媒體格式的編解碼器的名稱(chēng)景醇。
  3. 最后臀稚,使用createByCodecName(String)創(chuàng)建編解碼器。

2三痰、createDecoder/EncoderByType(String)

根據(jù)指定的MIME type來(lái)創(chuàng)建codec吧寺。

注意:使用這種方法創(chuàng)建的codec不能用于注入特征,并且可能創(chuàng)建無(wú)法處理特定所需媒體格式的編解碼器酒觅。
擴(kuò)展:
/**
* Sets whether a feature is to be enabled ({@code true}) or disabled
* ({@code false}).
*
* If {@code enabled} is {@code true}, the feature is requested to be present.
* Otherwise, the feature is requested to be not present.
*
* @param feature the name of a {@link MediaCodecInfo.CodecCapabilities} feature.
*
* @see MediaCodecList#findDecoderForFormat
* @see MediaCodecList#findEncoderForFormat
* @see MediaCodecInfo.CodecCapabilities#isFormatSupported
*/
public void setFeatureEnabled(@NonNull String feature, boolean enabled) {
setInteger(KEY_FEATURE_ + feature, enabled ? 1 : 0);
}

public void setFeatureEnabled (String feature, boolean enabled)
設(shè)置是啟用(true)還是禁用(false)功能撮执。 如果enabled為true微峰,則要求該功能存在舷丹。 否則,要求該功能不存在蜓肆。

兩個(gè)方案如下::

    private void createDecoderCodec() throws IOException {
        MediaExtractor mExtractor = new MediaExtractor();
        //extract /sdcard/test.mp4
        mExtractor.setDataSource("/sdcard/test.mp4");
        int trackCount = mExtractor.getTrackCount();
        for (int i = 0; i < trackCount; i++) {
            //1. 解碼文件或流時(shí)颜凯,可以從MediaExtractor#getTrackFormat獲得所需的格式。
            MediaFormat format = mExtractor.getTrackFormat(i);
            String mime = format.getString(MediaFormat.KEY_MIME);
            if (mime.startsWith("video/")) {
                //方案一
                //2. 使用MediaFormat#setFeatureEnabled注入要添加的所有特定功能,創(chuàng)建安全的解碼器仗扬。
                format.setFeatureEnabled(MediaCodecInfo.CodecCapabilities.FEATURE_SecurePlayback,true);
                //3. 然后調(diào)用MediaCodecList#findDecoderForFormat以獲取可以處理該特定媒體格式的編解碼器的名稱(chēng)症概。
                MediaCodecList list = new MediaCodecList(REGULAR_CODECS);
                String encoderForFormat = list.findDecoderForFormat(format);
                //4. 使用createByCodecName(String)創(chuàng)建編解碼器。
                MediaCodec codec = MediaCodec.createByCodecName(encoderForFormat);

                //方案二
                MediaCodec decoderByType = MediaCodec.createDecoderByType(mime);
            }
        }
    }

3早芭、創(chuàng)建安全的解碼器

在Android 4.4(KITKAT_WATCH)及之前版本彼城,安全的編解碼器沒(méi)有被列在MediaCodecList中,但是仍然可以在系統(tǒng)中使用。
安全編解碼器只能夠通過(guò)名字進(jìn)行實(shí)例化募壕,其名字是在常規(guī)編解碼器的名字后附加.secure標(biāo)識(shí)(所有安全編解碼器的名字都必須以.secure結(jié)尾)调炬,調(diào)用createByCodecName(String)方法創(chuàng)建安全編解碼器時(shí),如果系統(tǒng)中不存在指定名字的編解碼器就會(huì)拋出IOException異常舱馅。

從Android 5.0(LOLLIPOP)及之后版本缰泡,您應(yīng)該使用媒體格式的CodecCapabilities#FEATURE_SecurePlayback功能來(lái)創(chuàng)建安全的解碼器。

Initialization 初始化

根據(jù)之前我們提到的流程代嗤,創(chuàng)建完編解碼器之后棘钞,需要進(jìn)行configure。如果只是需要原始視頻數(shù)據(jù)干毅,直接configure就可以了宜猜。如果需要結(jié)合Surface使用,需要增加configuresurface參數(shù)硝逢。
如果要使用視頻使用者(用于處理原始視頻輸入的編解碼器宝恶,例如視頻編碼器)本地處理原始輸入視頻緩沖區(qū),請(qǐng)?jiān)谂渲煤笫褂?code>createInputSurface()為輸入數(shù)據(jù)創(chuàng)建目標(biāo)Surface趴捅。 或者垫毙,通過(guò)調(diào)用setInputSurface(Surface)將編解碼器設(shè)置為使用以前創(chuàng)建的持久輸入表面。

Codec-specific Data 編解碼器專(zhuān)用數(shù)據(jù)

某些格式拱绑,尤其是AAC音頻和MPEG4综芥,H.264和H.265視頻格式,要求實(shí)際數(shù)據(jù)的前綴是許多包含設(shè)置數(shù)據(jù)或編解碼器特定數(shù)據(jù)的緩沖區(qū)猎拨。

處理此類(lèi)壓縮格式時(shí)膀藐,必須在start()之后和任何幀數(shù)據(jù)之前將這些數(shù)據(jù)提交給編解碼器。 必須在對(duì)queueInputBuffer的調(diào)用中使用標(biāo)志BUFFER_FLAG_CODEC_CONFIG標(biāo)記此類(lèi)數(shù)據(jù)红省。

特定格式的編解碼器數(shù)據(jù)也可以包含在傳遞的格式中额各,以使用鍵“ csd-0”,“ csd-1”等在ByteBuffer條目中進(jìn)行配置吧恃。這些鍵始終包含在從MediaExtractor#getTrackFormat獲得的MediaFormat軌道中虾啦。
特定格式的編解碼器數(shù)據(jù)在start()時(shí)自動(dòng)提交給編解碼器, 您不得明確提交此數(shù)據(jù)。
如果該格式不包含編解碼器專(zhuān)用數(shù)據(jù)痕寓,則可以根據(jù)格式要求選擇使用正確數(shù)量的指定緩沖區(qū)使用指定數(shù)量提交傲醉。 對(duì)于H.264 AVC,您還可以連接所有特定于編解碼器的數(shù)據(jù)呻率,并將其作為單個(gè)編解碼器配置緩沖區(qū)提交硬毕。

Android使用以下特定于編解碼器的數(shù)據(jù)緩沖區(qū)。 為了正確配置MediaMuxer軌道礼仗,還需要將其設(shè)置為軌道格式吐咳。 每個(gè)參數(shù)集和標(biāo)有(*)的編解碼器專(zhuān)用數(shù)據(jù)部分必須以“ \ x00 \ x00 \ x00 \ x01”的起始代碼開(kāi)頭逻悠。


AAC audio, MPEG4, H.264韭脊, H.265 video格式的數(shù)據(jù)作為輸入源解碼的時(shí)候蹂风,需要指定一個(gè)特殊的前綴設(shè)置信息,這個(gè)信息通常包含在數(shù)據(jù)中乾蓬,但是需要自己提取出來(lái)惠啄,在mediacodec執(zhí)行start之后提交這些數(shù)據(jù),比如h264的sps和pps任内,在queueinputbuffer的時(shí)候flag設(shè)置為BUFFER_FLAG_CODEC_CONFIG提交給解碼器撵渡。同樣這些數(shù)據(jù)可以在configure的時(shí)候提交給mediacodec,效果和前邊的一樣:

mediaFormat.setByteBuffer("csd-0", ByteBuffer.wrap(sps));//sps是一個(gè)包含sps信息的byte數(shù)組
mediaFormat.setByteBuffer("csd-1", ByteBuffer.wrap(pps));//pps是一個(gè)包含pps信息的byte數(shù)組

當(dāng)調(diào)用start方法啟動(dòng)時(shí)這些信息同樣會(huì)傳給mediacodec死嗦。你絕不能直接提交這些數(shù)據(jù)趋距。 如果格式不包含編解碼器特定數(shù)據(jù),則可以根據(jù)格式要求越除,選擇使用指定數(shù)量的緩沖區(qū)以正確的順序提交它节腐。 在H.264 AVC的情況下,還可以連接所有編解碼器專(zhuān)用數(shù)據(jù)并將其作為單個(gè)編解碼器配置緩沖區(qū)提交摘盆。


下圖所有帶*的值必須加上前綴"\x00\x00\x00\x01"

注意:如果在返回任何輸出緩沖區(qū)或輸出格式更改之前立即或在啟動(dòng)后不久刷新編解碼器翼雀,則必須小心,因?yàn)榫幗獯a器特定的數(shù)據(jù)可能會(huì)在刷新過(guò)程中丟失孩擂。 刷新之后狼渊,必須使用標(biāo)有BUFFER_FLAG_CODEC_CONFIG的緩沖區(qū)重新提交數(shù)據(jù),以確保正確的編解碼器操作类垦。

編碼器(或生成壓縮數(shù)據(jù)的編解碼器)將在標(biāo)有codec-config標(biāo)志的輸出緩沖區(qū)中的任何有效輸出緩沖區(qū)之前狈邑,創(chuàng)建并返回特定于編解碼器的數(shù)據(jù)。 包含編解碼器特定數(shù)據(jù)的緩沖區(qū)沒(méi)有有意義的時(shí)間戳蚤认。

Data Processing 數(shù)據(jù)處理

每個(gè)codec都有一片屬于自己的輸入/輸出緩沖區(qū)米苹,每個(gè)緩沖區(qū)都有bufferID來(lái)指向。在調(diào)用start方法后砰琢,用戶(hù)不能訪(fǎng)問(wèn)任何的input bufferoutput buffer蘸嘶。

在同步模式下,調(diào)用dequeueInput / OutputBuffer(…)從編解碼器獲嚷任觥(或擁有)輸入或輸出緩沖區(qū)亏较。
在異步模式下莺褒,您將通過(guò)Callback#onInputBufferAvailable / Callback#onOutputBufferAvailable回調(diào)自動(dòng)接收可用緩沖區(qū)掩缓。

模式 輸入方法 輸出方法
同步模式 dequeueInputBuffer() dequeueOutputBuffer()
異步模式 MediaCodec.Callback.OnInputBufferAvailabe() MediaCodec.Callback.OnInputBufferAvailabe()

當(dāng)獲得inputbuffer(輸入方法執(zhí)行后)后,所有權(quán)交給了用戶(hù)遵岩,這些緩沖區(qū)由用戶(hù)填滿(mǎn)數(shù)據(jù)后需要使用queueInputBuffer(加密數(shù)據(jù)的話(huà)請(qǐng)使用queueSecureInputBuffer)提交緩沖區(qū)你辣,提交后緩沖區(qū)后所有權(quán)交給了codec巡通。注意不要為多個(gè)幀提供相同的時(shí)間戳,除非是配置信息舍哄,也就是標(biāo)記為BUFFER_FLAG_CODEC_CONFIG的幀可以隨意使用時(shí)間戳宴凉。

當(dāng)獲得outputbuffer(輸出方法執(zhí)行)后,用戶(hù)可訪(fǎng)問(wèn)一個(gè)只讀的緩沖區(qū)表悬,當(dāng)使用完畢后弥锄,請(qǐng)調(diào)用releaseOutputBuffer方法來(lái)將緩沖區(qū)返回給codec。

我們可以不立即queueinputbuffer/releaseOutputBuffer到編解碼器蟆沫,但用戶(hù)持有input/outputbuffer可能會(huì)使編解碼器停止工作籽暇,并且此行為取決于設(shè)備。 編解碼器有可能在產(chǎn)生輸出緩沖區(qū)之前暫停饭庞,直到所有未完成的緩沖區(qū)queueinputbuffer/releaseOutputBuffer戒悠。 因此,用戶(hù)最好每次獲得緩沖區(qū)后執(zhí)行釋放操作舟山。

根據(jù)API版本绸狐,你可以用三種方式處理數(shù)據(jù):

處理數(shù)據(jù)的方式

Asynchronous Processing using Buffers 使用緩沖區(qū)的異步處理

從LOLLIPOP開(kāi)始,首選方法是在調(diào)用configure方法之前通過(guò)設(shè)置回調(diào)來(lái)異步處理數(shù)據(jù)累盗。 異步模式會(huì)稍微改變狀態(tài)轉(zhuǎn)換步驟寒矿,在running狀態(tài)時(shí)必須在調(diào)用flush()之后調(diào)用start()方法,將編解碼器轉(zhuǎn)換為Running子狀態(tài)并開(kāi)始接收輸入緩沖區(qū)若债。 同樣劫窒,在初始調(diào)用開(kāi)始時(shí),codec將直接移至Running子狀態(tài)拆座,并通過(guò)回調(diào)開(kāi)始傳遞可用的輸入緩沖區(qū)主巍。
官方解釋如下圖:


使用緩沖區(qū)的異步處理

MediaCodec通常在異步模式下這樣使用:

 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();

設(shè)置回調(diào)方法必須在mediacodec創(chuàng)建之后,并且在configure方法之前挪凑。

MediaCodec.Callback共有四個(gè)方法:

void onError(MediaCodec codec, MediaCodec.CodecException e)//發(fā)生錯(cuò)誤時(shí)回調(diào)此方法

void onInputBufferAvailable(MediaCodec codec, int index)//當(dāng)inputbuffer可用時(shí)回調(diào)此方法

void onOutputBufferAvailable(MediaCodec codec, int index, MediaCodec.BufferInfo info)
//當(dāng)output方法可用時(shí)回調(diào)此方法
void onOutputFormatChanged(MediaCodec codec, MediaFormat format)//當(dāng)輸出格式變化時(shí)回調(diào)此方法

看一下幾個(gè)重要的方法:

  1. ByteBuffer getInputBuffer (int index)

該方法會(huì)返回一個(gè)已清空孕索、可寫(xiě)入的input緩沖區(qū),通過(guò)調(diào)用ByteBuffer.put(data)方法將data中的數(shù)據(jù)放到緩沖區(qū)后,也可以進(jìn)行其他處理躏碳,然后調(diào)用void queueInputBuffer (int index, int offset, int size, long presentationTimeUs, int flags)就可以將緩沖區(qū)返回給codec搞旭。index是回調(diào)函數(shù)中返回的index,offset是緩沖區(qū)提交數(shù)據(jù)的起始未知菇绵,可以不從0開(kāi)始肄渗,size是需要提交的長(zhǎng)度,presentationTimeUs是時(shí)間戳咬最,這個(gè)時(shí)間戳最好是按幀率來(lái)計(jì)算(單位:ns)翎嫡,當(dāng)使用surface作為輸出時(shí),這個(gè)時(shí)間會(huì)作為視頻的時(shí)間戳來(lái)顯示永乌;flags一般三個(gè)值:BUFFER_FLAG_CODEC_CONFIG:配置信息惑申,BUFFER_FLAG_END_OF_STREAM:結(jié)束標(biāo)志具伍,BUFFER_FLAG_KEY_FRAME:關(guān)鍵幀,不建議使用圈驼。
在執(zhí)行此方法后index指向的緩沖區(qū)將不可訪(fǎng)問(wèn)人芽,繼續(xù)使用將會(huì)拋出異常。

  1. ByteBuffer getOutputBuffer (int index)

用法同getinputbuffer一樣绩脆。

  1. void releaseOutputBuffer (int index,boolean render)
    void releaseOutputBuffer (int index,long renderTimestampNs)

這兩個(gè)方法都會(huì)釋放index所指向的緩沖區(qū)萤厅。
假如使用了surface,第二個(gè)參數(shù)傳入傳入true將會(huì)把數(shù)據(jù)先輸出給surface靴迫,當(dāng)surface不再使用時(shí)立即返回給codec祈坠,傳入long型時(shí):
如果在SurfaceView上渲染緩沖區(qū),則可以使用時(shí)間戳在特定時(shí)間渲染緩沖區(qū)(在緩沖區(qū)時(shí)間戳之后或之后的VSYNC處)矢劲。為了達(dá)到這個(gè)目的赦拘,時(shí)間戳需要合理地接近當(dāng)前的nanoTime()。目前芬沉,這是在(1)秒內(nèi)設(shè)定的躺同。

一些注意事項(xiàng):
該緩沖區(qū)將不會(huì)返回到編解碼器,直到時(shí)間戳已經(jīng)過(guò)去并且該緩沖區(qū)不再被Surface使用丸逸。
緩沖區(qū)會(huì)按順序處理蹋艺,因此您可能會(huì)阻止后續(xù)緩沖區(qū)顯示在Surface上。如果您想對(duì)用戶(hù)操作做出反應(yīng)黄刚,這很重要捎谨。停止視頻或?qū)で蟆?br> 如果將多個(gè)緩沖區(qū)發(fā)送到要在同一個(gè)VSYNC上渲染的Surface,則會(huì)顯示最后一個(gè)緩沖區(qū)憔维,其他將被放棄涛救。
如果時(shí)間戳不與當(dāng)前系統(tǒng)時(shí)間“合理接近”,Surface將忽略時(shí)間戳业扒,并在最早的可行時(shí)間顯示緩沖區(qū)检吆。在這種模式下,它不會(huì)丟幀程储。
為獲得最佳性能和質(zhì)量蹭沛,當(dāng)您在所需渲染時(shí)間之前約兩個(gè)VSYNC的時(shí)間時(shí)調(diào)用此方法。對(duì)于60Hz的顯示器章鲤,這是大約33毫秒摊灭。
這段話(huà)的大概意思就是不要使用這個(gè)方法。

Synchronous Processing using Buffers 使用緩沖區(qū)的同步處理

從LOLLIPOP開(kāi)始败徊,即使在同步模式下使用編解碼器帚呼,也應(yīng)使用getInput / OutputBuffer(int)和/或getInput / OutputImage(int)檢索輸入和輸出緩沖區(qū)。 這允許框架進(jìn)行某些優(yōu)化集嵌,例如 處理動(dòng)態(tài)內(nèi)容時(shí)萝挤。 如果調(diào)用getInput / OutputBuffers()御毅,則會(huì)禁用此優(yōu)化根欧。

MediaCodec通常在同步模式下這樣使用:

MediaCodec codec = MediaCodec.createByCodecName(name);
 codec.configure(format, …);
 MediaFormat outputFormat = codec.getOutputFormat(); // option B
 codec.start();
 for (;;) {
  int inputBufferId = codec.dequeueInputBuffer(timeoutUs);
  if (inputBufferId >= 0) {
    ByteBuffer inputBuffer = codec.getInputBuffer(…);
    // fill inputBuffer with valid data
    …
    codec.queueInputBuffer(inputBufferId, …);
  }
  int outputBufferId = codec.dequeueOutputBuffer(…);
  if (outputBufferId >= 0) {
    ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId);
    MediaFormat bufferFormat = codec.getOutputFormat(outputBufferId); // option A
    // bufferFormat is identical to outputFormat
    // outputBuffer is ready to be processed or rendered.
    …
    codec.releaseOutputBuffer(outputBufferId, …);
  } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
    // Subsequent data will conform to new format.
    // Can ignore if using getOutputFormat(outputBufferId)
    outputFormat = codec.getOutputFormat(); // option B
  }
 }
 codec.stop();
 codec.release();

看一下重要方法:

int dequeueInputBuffer (long timeoutUs)
返回值是緩沖區(qū)的BufferId怜珍,假如返回值為-1則表示緩沖區(qū)不能使用。傳入的參數(shù)為正凤粗,則是最長(zhǎng)等待時(shí)間酥泛,為0則會(huì)立即返回緩沖區(qū)的id,負(fù)數(shù)則會(huì)無(wú)限等待嫌拣。

Synchronous Processing using Buffer Arrays (deprecated) 使用緩沖區(qū)數(shù)組的同步處理(不建議使用)

Build.VERSION_CODES.KITKAT_WATCH及更低版本中柔袁,輸入和輸出緩沖區(qū)的集合由ByteBuffer []數(shù)組表示。 成功調(diào)用start()后异逐,使用getInput / OutputBuffers()檢索緩沖區(qū)數(shù)組捶索。 使用緩沖區(qū)ID作為這些數(shù)組的索引(非負(fù)數(shù)時(shí)),如以下示例所示灰瞻。 請(qǐng)注意腥例,盡管數(shù)組大小提供了上限,但數(shù)組大小與系統(tǒng)使用的輸入和輸出緩沖區(qū)的數(shù)量之間沒(méi)有固有的相關(guān)性酝润。

MediaCodec通常在同步模式下這樣使用:

MediaCodec codec = MediaCodec.createByCodecName(name);
 codec.configure(format, …);
 codec.start();
 ByteBuffer[] inputBuffers = codec.getInputBuffers();
 ByteBuffer[] outputBuffers = codec.getOutputBuffers();
 for (;;) {
  int inputBufferId = codec.dequeueInputBuffer(…);
  if (inputBufferId >= 0) {
    // fill inputBuffers[inputBufferId] with valid data
    …
    codec.queueInputBuffer(inputBufferId, …);
  }
  int outputBufferId = codec.dequeueOutputBuffer(…);
  if (outputBufferId >= 0) {
    // outputBuffers[outputBufferId] is ready to be processed or rendered.
    …
    codec.releaseOutputBuffer(outputBufferId, …);
  } else if (outputBufferId == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
    outputBuffers = codec.getOutputBuffers();
  } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
    // Subsequent data will conform to new format.
    MediaFormat format = codec.getOutputFormat();
  }
 }
 codec.stop();
 codec.release();

End-of-stream

當(dāng)結(jié)束輸入數(shù)據(jù)時(shí)燎竖,發(fā)送如下代碼即可:

codec.queueInputBuffer(index,0,0,0,BUFFER_FLAG_END_OF_STREAM); //第三個(gè)時(shí)間戳可以隨意設(shè)置,空緩沖區(qū)的時(shí)間戳被忽略。

接受到這個(gè)信號(hào)后要销,codec將不再接受任何新的數(shù)據(jù)构回,在這個(gè)信號(hào)之前的數(shù)據(jù)會(huì)全部輸出。
除非已刷新疏咐,停止或重新啟動(dòng)編解碼器纤掸,否則請(qǐng)?jiān)诎l(fā)出輸入流結(jié)束信號(hào)后不要提交其他輸入緩沖區(qū)。

Using an Output Surface 使用surface作為輸出

使用surface做為輸出時(shí)與使用Bytebuffer基本一致浑塞,只是在surface模式下所有的bytebuffer和image全部為null茁肠。

releaseOutputBuffer(bufferId, false);  //不會(huì)渲染到surface上
releaseOutputBuffer(bufferId, true); //使用默認(rèn)的時(shí)間戳渲染視頻
releaseOutputBuffer(bufferId, timestamp) //使用指定的時(shí)間戳渲染視頻

Build.VERSION_CODES.M開(kāi)始,默認(rèn)時(shí)間戳為緩沖區(qū)的BufferInfo#presentationTimeUs(轉(zhuǎn)換為納秒)缩举。 在此之前未定義垦梆。

Using an Input Surface 使用surface作為輸入

使用surface作為輸入,沒(méi)有可訪(fǎng)問(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é)束京腥。 調(diào)用后,輸入Surface將立即停止向編解碼器提交數(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