Android媒体解码MediaCodec,MediaExtractor
生活随笔
收集整理的這篇文章主要介紹了
Android媒体解码MediaCodec,MediaExtractor
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
Android提供了MediaPlayer播放器播放媒體文件,其實MediaPlyer只是對Android Media包下的MediaCodec和MediaExtractor進行了包裝,方便使用。但是最好理解下Android媒體文件的解碼,編碼和渲染流程。
Shape Of My Heart.mp4
<source src="http://7xoquj.com1.z0.glb.clouddn.com/shape_of_my_heart.mp4" type="video/mp4">使用android.media包下的MediaCodec和MediaExtractor實現一個簡單的視頻解碼渲染。
使用到了:
- MediaCodec:負責媒體文件的編碼和解碼工作,內部方法均為native
- MediaExtractor:負責將指定類型的媒體文件從文件中找到軌道,并填充到MediaCodec的緩沖區中
- AudioTrack:負責將解碼之后的音頻播放
- SurfaceView:展示解碼之后的視頻
視頻被播放主要分為以下步驟:
音頻的播放類似,只多了AudioTrack部分,少了渲染到surface部分。
MediaCodec.releaseOutputBuffer(int outputBufferIndex, boolean render);
- render為true就會渲染到surface
播放的控制,視頻和音頻各自擁有一個Thread。
public void play() {isPlaying = true;if (videoThread == null) {videoThread = new VideoThread();videoThread.start();}if (audioThread == null) {audioThread = new AudioThread();audioThread.start();}}public void stop() {isPlaying = false;}VideoThread
private class VideoThread extends Thread {@Overridepublic void run() {MediaExtractor videoExtractor = new MediaExtractor();MediaCodec videoCodec = null;try {videoExtractor.setDataSource(filePath);} catch (IOException e) {e.printStackTrace();}int videoTrackIndex;//獲取視頻所在軌道videoTrackIndex = getMediaTrackIndex(videoExtractor, "video/");if (videoTrackIndex >= 0) {MediaFormat mediaFormat = videoExtractor.getTrackFormat(videoTrackIndex);int width = mediaFormat.getInteger(MediaFormat.KEY_WIDTH);int height = mediaFormat.getInteger(MediaFormat.KEY_HEIGHT);//視頻長度:秒float time = mediaFormat.getLong(MediaFormat.KEY_DURATION) / 1000000;callBack.videoAspect(width, height, time);videoExtractor.selectTrack(videoTrackIndex);try {videoCodec = MediaCodec.createDecoderByType(mediaFormat.getString(MediaFormat.KEY_MIME));videoCodec.configure(mediaFormat, surface, null, 0);} catch (IOException e) {e.printStackTrace();}}if (videoCodec == null) {Log.v(TAG, "MediaCodec null");return;}videoCodec.start();MediaCodec.BufferInfo videoBufferInfo = new MediaCodec.BufferInfo();ByteBuffer[] inputBuffers = videoCodec.getInputBuffers(); // ByteBuffer[] outputBuffers = videoCodec.getOutputBuffers();boolean isVideoEOS = false;long startMs = System.currentTimeMillis();while (!Thread.interrupted()) {if (!isPlaying) {continue;}//將資源傳遞到解碼器if (!isVideoEOS) {isVideoEOS = putBufferToCoder(videoExtractor, videoCodec, inputBuffers);}int outputBufferIndex = videoCodec.dequeueOutputBuffer(videoBufferInfo, TIMEOUT_US);switch (outputBufferIndex) {case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:Log.v(TAG, "format changed");break;case MediaCodec.INFO_TRY_AGAIN_LATER:Log.v(TAG, "解碼當前幀超時");break;case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED://outputBuffers = videoCodec.getOutputBuffers();Log.v(TAG, "output buffers changed");break;default://直接渲染到Surface時使用不到outputBuffer//ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];//延時操作//如果緩沖區里的可展示時間>當前視頻播放的進度,就休眠一下sleepRender(videoBufferInfo, startMs);//渲染videoCodec.releaseOutputBuffer(outputBufferIndex, true);break;}if ((videoBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {Log.v(TAG, "buffer stream end");break;}}//end whilevideoCodec.stop();videoCodec.release();videoExtractor.release();}}獲取指定類型媒體文件所在軌道
//獲取指定類型媒體文件所在軌道private int getMediaTrackIndex(MediaExtractor videoExtractor, String MEDIA_TYPE) {int trackIndex = -1;for (int i = 0; i < videoExtractor.getTrackCount(); i++) {//獲取視頻所在軌道MediaFormat mediaFormat = videoExtractor.getTrackFormat(i);String mime = mediaFormat.getString(MediaFormat.KEY_MIME);if (mime.startsWith(MEDIA_TYPE)) {trackIndex = i;break;}}return trackIndex;}將緩沖區傳遞至解碼器
//將緩沖區傳遞至解碼器private boolean putBufferToCoder(MediaExtractor extractor, MediaCodec decoder, ByteBuffer[] inputBuffers) {boolean isMediaEOS = false;int inputBufferIndex = decoder.dequeueInputBuffer(TIMEOUT_US);if (inputBufferIndex >= 0) {ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];int sampleSize = extractor.readSampleData(inputBuffer, 0);if (sampleSize < 0) {decoder.queueInputBuffer(inputBufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);isMediaEOS = true;Log.v(TAG, "media eos");} else {decoder.queueInputBuffer(inputBufferIndex, 0, sampleSize, extractor.getSampleTime(), 0);extractor.advance();}}return isMediaEOS;}音頻的部分類似,完整源碼請移步jiyangg/MediaPlaySimpleDemo
轉載于:https://www.cnblogs.com/jiy-for-you/p/7282033.html
總結
以上是生活随笔為你收集整理的Android媒体解码MediaCodec,MediaExtractor的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: codevs 1283 等差子序列
- 下一篇: With you With me