androidmediacodec解碼
A. android使用mediacodec進行編解碼 常用嗎
應該還是比較常用的。示例如下:
java">privatefinalStringTAG="MediaCodeSample";
/**用來解碼*/
privateMediaCodecmMediaCodec;
/**用來讀取音頻文件*/
;
privateMediaFormatformat;
privateStringmime=null;
privateintsampleRate=0,channels=0,bitrate=0;
privatelongpresentationTimeUs=0,ration=0;
publicvoiddecode(Stringurl)
{
extractor=newMediaExtractor();
//根據路徑獲取源文件
try
{
extractor.setDataSource(url);
}catch(Exceptione)
{
Log.e(TAG,"設置文件路徑錯誤"+e.getMessage());
}
try
{
//音頻文件信息
format=extractor.getTrackFormat(0);
mime=format.getString(MediaFormat.KEY_MIME);
sampleRate=format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
//聲道個數:單聲道或雙聲道
channels=format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
//ifrationis0,
ration=format.getLong(MediaFormat.KEY_DURATION);
//System.out.println("歌曲總時間秒:"+ration/1000000);
bitrate=format.getInteger(MediaFormat.KEY_BIT_RATE);
}catch(Exceptione)
{
Log.e(TAG,"音頻文件信息讀取出錯:"+e.getMessage());
//不要退出,下面進行判斷
}
Log.d(TAG,"Trackinfo:mime:"+mime+"采樣率sampleRate:"+sampleRate+"channels:"+channels+"bitrate:"
+bitrate+"ration:"+ration);
//檢查是否為音頻文件
if(format==null||!mime.startsWith("audio/"))
{
Log.e(TAG,"不是音頻文件end!");
return;
}
//實例化一個指定類型的解碼器,提供數據輸出
//
mMediaCodec=MediaCodec.createDecoderByType(mime);
if(mMediaCodec==null)
{
Log.e(TAG,"創建解碼器失敗!");
return;
}
mMediaCodec.configure(format,null,null,0);
mMediaCodec.start();
//用來存放目標文件的數據
ByteBuffer[]inputBuffers=mMediaCodec.getInputBuffers();
//解碼後的數據
ByteBuffer[]outputBuffers=mMediaCodec.getOutputBuffers();
//設置聲道類型:AudioFormat.CHANNEL_OUT_MONO單聲道,AudioFormat.CHANNEL_OUT_STEREO雙聲道
intchannelConfiguration=channels==1?AudioFormat.CHANNEL_OUT_MONO:AudioFormat.CHANNEL_OUT_STEREO;
Log.i(TAG,"channelConfiguration="+channelConfiguration);
extractor.selectTrack(0);
//==========開始解碼=============
booleansawInputEOS=false;
booleansawOutputEOS=false;
finallongkTimeOutUs=10;
MediaCodec.BufferInfoinfo=newMediaCodec.BufferInfo();
while(!sawOutputEOS)
{
try
{
if(!sawInputEOS)
{
intinputBufIndex=mMediaCodec.dequeueInputBuffer(kTimeOutUs);
if(inputBufIndex>=0)
{
ByteBufferdstBuf=inputBuffers[inputBufIndex];
intsampleSize=extractor.readSampleData(dstBuf,0);
if(sampleSize<0)
{
Log.d(TAG,"sawinputEOS.Stoppingplayback");
sawInputEOS=true;
sampleSize=0;
}else
{
presentationTimeUs=extractor.getSampleTime();
}
mMediaCodec.queueInputBuffer(inputBufIndex,0,sampleSize,presentationTimeUs,
sawInputEOS?MediaCodec.BUFFER_FLAG_END_OF_STREAM:0);
if(!sawInputEOS)
{
extractor.advance();
}
}else
{
Log.e(TAG,"inputBufIndex"+inputBufIndex);
}
}//!sawInputEOS
//
intres=mMediaCodec.dequeueOutputBuffer(info,kTimeOutUs);
if(res>=0)
{
intoutputBufIndex=res;
ByteBufferbuf=outputBuffers[outputBufIndex];
finalbyte[]chunk=newbyte[info.size];
buf.get(chunk);
buf.clear();
if(chunk.length>0)
{
//chunk解碼後的音頻流
//TODO:處理...
}
mMediaCodec.releaseOutputBuffer(outputBufIndex,false);
if((info.flags&MediaCodec.BUFFER_FLAG_END_OF_STREAM)!=0)
{
Log.d(TAG,"sawoutputEOS.");
sawOutputEOS=true;
}
}elseif(res==MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED)
{
outputBuffers=mMediaCodec.getOutputBuffers();
Log.w(TAG,"[AudioDecoder]outputbuffershavechanged.");
}elseif(res==MediaCodec.INFO_OUTPUT_FORMAT_CHANGED)
{
MediaFormatoformat=mMediaCodec.getOutputFormat();
Log.w(TAG,"[AudioDecoder]outputformathaschangedto"+oformat);
}else
{
Log.w(TAG,"[AudioDecoder]dequeueOutputBufferreturned"+res);
}
}catch(RuntimeExceptione)
{
Log.e(TAG,"[decodeMP3]error:"+e.getMessage());
}
}
//=================================================================================
if(mMediaCodec!=null)
{
mMediaCodec.stop();
mMediaCodec.release();
mMediaCodec=null;
}
if(extractor!=null)
{
extractor.release();
extractor=null;
}
//clearsourceandtheotherglobals
ration=0;
mime=null;
sampleRate=0;
channels=0;
bitrate=0;
presentationTimeUs=0;
ration=0;
}
B. android使用mediacodec進行編解碼 常用嗎
應該還是比較常用的。示例如下:
private final String TAG = "MediaCodeSample";
/** 用來解碼 */
private MediaCodec mMediaCodec;
/** 用來讀取音頻文件 */
private MediaExtractor extractor;
private MediaFormat format;
private String mime = null;
private int sampleRate = 0, channels = 0, bitrate = 0;
private long presentationTimeUs = 0, ration = 0;
public void decode(String url)
{
extractor = new MediaExtractor();
// 根據路徑獲取源文件
try
{
extractor.setDataSource(url);
} catch (Exception e)
{
Log.e(TAG, " 設置文件路徑錯誤" + e.getMessage());
}
try
{
// 音頻文件信息
format = extractor.getTrackFormat(0);
mime = format.getString(MediaFormat.KEY_MIME);
sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
// 聲道個數:單聲道或雙聲道
channels = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
// if ration is 0, we are probably playing a live stream
ration = format.getLong(MediaFormat.KEY_DURATION);
// System.out.println("歌曲總時間秒:"+ration/1000000);
bitrate = format.getInteger(MediaFormat.KEY_BIT_RATE);
} catch (Exception e)
{
Log.e(TAG, "音頻文件信息讀取出錯:" + e.getMessage());
// 不要退出,下面進行判斷
}
Log.d(TAG, "Track info: mime:" + mime + " 采樣率sampleRate:" + sampleRate + " channels:" + channels + " bitrate:"
+ bitrate + " ration:" + ration);
// 檢查是否為音頻文件
if (format == null || !mime.startsWith("audio/"))
{
Log.e(TAG, "不是音頻文件 end !");
return;
}
// 實例化一個指定類型的解碼器,提供數據輸出
// Instantiate an encoder supporting output data of the given mime type
mMediaCodec = MediaCodec.createDecoderByType(mime);
if (mMediaCodec == null)
{
Log.e(TAG, "創建解碼器失敗!");
return;
}
mMediaCodec.configure(format, null, null, 0);
mMediaCodec.start();
// 用來存放目標文件的數據
ByteBuffer[] inputBuffers = mMediaCodec.getInputBuffers();
// 解碼後的數據
ByteBuffer[] outputBuffers = mMediaCodec.getOutputBuffers();
// 設置聲道類型:AudioFormat.CHANNEL_OUT_MONO單聲道,AudioFormat.CHANNEL_OUT_STEREO雙聲道
int channelConfiguration = channels == 1 ? AudioFormat.CHANNEL_OUT_MONO : AudioFormat.CHANNEL_OUT_STEREO;
Log.i(TAG, "channelConfiguration=" + channelConfiguration);
extractor.selectTrack(0);
// ==========開始解碼=============
boolean sawInputEOS = false;
boolean sawOutputEOS = false;
final long kTimeOutUs = 10;
MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
while (!sawOutputEOS)
{
try
{
if (!sawInputEOS)
{
int inputBufIndex = mMediaCodec.dequeueInputBuffer(kTimeOutUs);
if (inputBufIndex >= 0)
{
ByteBuffer dstBuf = inputBuffers[inputBufIndex];
int sampleSize = extractor.readSampleData(dstBuf, 0);
if (sampleSize < 0)
{
Log.d(TAG, "saw input EOS. Stopping playback");
sawInputEOS = true;
sampleSize = 0;
} else
{
presentationTimeUs = extractor.getSampleTime();
}
mMediaCodec.queueInputBuffer(inputBufIndex, 0, sampleSize, presentationTimeUs,
sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
if (!sawInputEOS)
{
extractor.advance();
}
} else
{
Log.e(TAG, "inputBufIndex " + inputBufIndex);
}
} // !sawInputEOS
// decode to PCM and push it to the AudioTrack player
int res = mMediaCodec.dequeueOutputBuffer(info, kTimeOutUs);
if (res >= 0)
{
int outputBufIndex = res;
ByteBuffer buf = outputBuffers[outputBufIndex];
final byte[] chunk = new byte[info.size];
buf.get(chunk);
buf.clear();
if (chunk.length > 0)
{
// chunk解碼後的音頻流
// TODO:處理...
}
mMediaCodec.releaseOutputBuffer(outputBufIndex, false);
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0)
{
Log.d(TAG, "saw output EOS.");
sawOutputEOS = true;
}
} else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED)
{
outputBuffers = mMediaCodec.getOutputBuffers();
Log.w(TAG, "[AudioDecoder]output buffers have changed.");
} else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED)
{
MediaFormat oformat = mMediaCodec.getOutputFormat();
Log.w(TAG, "[AudioDecoder]output format has changed to " + oformat);
} else
{
Log.w(TAG, "[AudioDecoder] dequeueOutputBuffer returned " + res);
}
} catch (RuntimeException e)
{
Log.e(TAG, "[decodeMP3] error:" + e.getMessage());
}
}
// =================================================================================
if (mMediaCodec != null)
{
mMediaCodec.stop();
mMediaCodec.release();
mMediaCodec = null;
}
if (extractor != null)
{
extractor.release();
extractor = null;
}
// clear source and the other globals
ration = 0;
mime = null;
sampleRate = 0;
channels = 0;
bitrate = 0;
presentationTimeUs = 0;
ration = 0;
}
C. Android MediaCodec
MediaCodec 類為開發者提供了能訪問到Android底層媒體 Codec (Encoder/Decoder)的能力,它是Android底層多媒體基礎架構的一部分(通常和MediaExtractor、MediaSync、MediaMuxer、MediaCrypto、MediaDrm、Image、Surface、AudioTrack一起使用)。
Codec 對三種類型類型的數據起作用: 編碼後的壓縮數據 , 原始視頻數據 , 原始音頻數據 。這三種類型的數據都可以通過 ByteBuffer 來傳遞給 Codec ,但是對於 原始視頻數據 我們建議使用 Surface 來傳遞,這樣可以提高 Codec 的性能, Surface 使用的是 native video buffer ,不用映射或者拷貝成 ByteBuffer ,因此這樣的方式更高效。當你使用 Surface 來傳遞 原始視頻數據 時,也就無法獲取到了 原始視頻數據 ,Android 提供了 ImageReader 幫助你獲取到解碼後的 原始視頻數據 。這種方式可能仍然有要比 ByteBuffer 的方式更加高效,因為某些 native video buffer 會直接映射成 byteBuffer 。當然如果你 ByteBuffer 的模式,你可以使用 Image 類提供的 getInput/OutputImage(int) 來獲取 原始視頻數據 。
給 Decoder 輸入的 InputBuffer 或者 Encoder 輸出的 outputBuffer 包含的都是編碼後的壓縮數據,數據的壓縮類型由 MediaFormat#KEY_MIME 指明。對於視頻類型而言,這個數據通常是一個壓縮後的視頻幀。對於音頻數據而言,通常是一個訪問單元(一個編碼的音頻段,通常包含幾毫秒的音頻數據,數據類型format type 指定),有時候,一個音頻單元對於一個 buffer 而言可能有點寬松,所以一個 buffer 里可能包含多個編碼後的音頻數據單元。無論 Buffer 包含的是視頻數據還是音頻數據, Buffer 都不會再任意位元組邊界上開始或者結束,而是在幀(視頻)或者單元(音頻)的邊界上開始或者結束。除非它們被BUFFER_FLAG_PARTIAL_FRAME標記。
原始音頻Buffer包含PCM音頻數據的整個幀,是每一個通道按著通道順序的采樣數據。每一個采樣按16Bit量化。
在 ByteBuffer 模式下,視頻數據的排布由 MediaFormat#KEY_COLOR_FORMAT 指定,我們可以通過 getCodecInfo().MediaCodecInfo#getCapabilitiesForType.CodecCapabilities#colorFormats 獲取到一個設備支持的 color format 數組。視頻 Codec 可能支持三種類型的Color Format:
從 Build.VERSION_CODES.LOLLIPOP_MR1 開始所有的視頻 Codec 都支持 flexible YUV 4:2:0
對於 Build.VERSION_CODES.LOLLIPOP 之前並且支持 Image 類時,我們需要使用 MediaFormat#KEY_STRIDE 和 MediaFormat#KEY_SLICE_HEIGHT 的值去理解輸出的原始視頻數據的布局。
鍵值 MediaFormat#KEY_WIDTH 和 MediaFormat#KEY_HEIGHT 指明了視頻Frame的size。然而,對於大多數用於編碼的視頻圖像,他們只佔用了video Frame的一部分。這部分用一個 'crop rectangle 來表示。
我們需要用下面的一些 keys 從獲取原始視頻數據的 crop rectangle ,如果 out format 中沒有包含這些 keys ,則表示視頻占據了整個 video Frame ,這個 crop rectangle 的解釋應該立足於應用任何 MediaFormat#KEY_ROTATION 之前。
下面是在旋轉之前計算視頻的尺寸的案例:
從概念上講Codec的聲明周期存在三種狀態: Stoped , Executing , Released 。 Stoped 狀態是一個集合狀態,它聚合了三種狀態: Uninitialized , Configured ,和 Error ,同時 Executing 狀態的處理也是通過三個子狀態來完成: Flushed , Running , End-of-Stream 。
Executing 狀態有三個子狀態:Flushed,Running,和End-of-Stream,當我們調用玩 Start() 函數後, Codec 就立刻進入 Flushed 子狀態,這個狀態下,它持有全部的buffer,只要第一個Input buffer被dequeued,Codec就轉變成 Running 子狀態,這個狀態占據了 Codec 的生命周期的絕大部分。當入隊一個帶有 end-of-stream標志的InputBuffer後, Codec 將轉換成 End of Stream 子狀態,在這個狀態下, Codec 將不會再接收任何輸入的數據,但是仍然會產生output buffer ,直到end-of-Stream標記的buffer被輸出。我們可以在 Executing 狀態的任何時候,使用 flush() 函數,將 Codec 切換成 Flushed 狀態。
調用 stop() 函數會將 Codec 返回到 Uninitialized 狀態,這樣我們就可以對 Codec 進行重新配置,當你用完了 Codec 後,你必須要調用 release() 函數去釋放這個 Codec 。
在極少數情況下, Codec 可能也會遇到錯誤,此時 Codec 將會切換到 Error 狀態,我們可以通過queuing操作獲取到一個無效的返回值,或者有時會通過異常來的得知 Codec 發生了錯誤。通過調用 reset() 函數,將 Codec 進行重置,這樣 Codec 將切換成 Uninitalized 狀態,我們可以在任何狀態下調用 rest() 函數將Codec 將切換成 Uninitalized`狀態。
使用 MediaCodecList 創建一個指定 MediaFormat 的MediaCodec。當我們解碼一個文件或者一個流時,我們可以通過 MediaExtractor#getTrackFormat 獲取期望的Fromat,同時我們可以通過 MediaFormat#setFeatureEnabled 為 Codec 注入任何我們想要的特性。然後調用 MediaCodecList#findDecoderForFormat 獲取能夠處理對應format數據 Codec 的name,最後我們使用 createByCodecName(String) 創建出這個 Codec 。
我們也可以使用 createDecoder/EncoderByType(java.lang.String) 函數來創建指定的 MIME 類型的 Codec ,但是這樣我們無法向其中注入一些指定的特性,這樣創建的 Codec 可能不能處理我們期望的媒體類型數據。
D. android使用MediaCodec進行解碼,就是硬解碼嗎
Android api中有自帶的硬解碼方法MediaCodec,但是SDK並沒有規定輸出格式,參考H264的解碼格式
E. 手機自帶mediacodec嗎
手機自帶mediacodec。MediaCodec 是 Android 中的編解碼器組件,用來訪問底層提供的編解碼器,手機是自帶mediacodec的。
通常與MediaExtractor、MediaSync、MediaMuxer、MediaCrypto、MediaDrm、Image、Surface和AudioTrack一起使用,MediaCodec幾乎是Android播放器硬解碼的標配。
多媒體晶元的發展
在1996年MMX CPU問世之際,即打亂了多媒體晶元發展的時間表。被英特爾視為近10年來CPU革命性進展的大作MMX,藉由CPU中新增的57個多媒體指令集、8個64位MMX暫存器及32 KB的Cache。
不僅提升了多媒體及通信程序的執行性能,亦顛覆了多媒體及通信產業的生態環境,且以後CPU亦將遵循此方向發展。
軟體MPEG將取代硬體MPEGMPEG—l硬體解決方案在CPU功能較強的情況下,被軟體MPEG取代掉了。MPEG2亦面臨岌岌可危之境。MPEG硬體將謀求PC領域以外的新世界,轉向進攻消費性電子產品。
低級Audio將遭受危機MMX CPU取代音效卡。此時強調高品質、高層次的音效晶元將有脫穎而出的機會。未來可能將以On board或IC整合的形式,對音效卡廠商造成不小的沖擊,從而思索未來轉型之道。
性能的提高MMX旨在增進繪圖卡性能,而AGP也已成為提升系統晶元組與繪圖晶元問的橋梁。因此,繪圖卡將仍會持續,在繪圖晶元上將朝影像、動畫、圖形加速、3D方向整合MMX,輔以影像繪圖加速j棗片將使PC有更大的發揮。