Android媒體解碼MediaCodec MediaExtractor學習

來源:互聯網
上載者:User

標籤:步驟   ref   creat   tag   mem   err   資源   actor   輸入   

Android提供了MediaPlayer播放器播放媒體檔案,其實MediaPlyer只是對Android Media包下的MediaCodec和MediaExtractor進行了封裝,方便使用。但是最好理解下Android媒體檔案的解碼,編碼和渲染流程。

使用android.media包下的MediaCodec和MediaExtractor實現一個簡單的視頻解碼渲染。

使用到了:

  • MediaCodec:負責媒體檔案的編碼和解碼工作,內部方法均為native方法
  • MediaExtractor:負責將指定類型的媒體檔案從檔案中找到軌道,並填充到MediaCodec的緩衝區中,內部方法均為native方法

  • AudioTrack:負責將解碼之後的音頻播放
  • SurfaceView:展示解碼之後的視頻

視頻被播放主要分為以下步驟:

  1. 將資源載入到extractor
  2. 擷取視頻所在軌道
  3. 設定extractor選中視頻所在軌道
  4. 創將解碼視頻的MediaCodec,decoder
  5. 開始迴圈,直到視頻資源的末尾
  6. 將extractor中資源以一個單位填充進decoder的輸入緩衝區
  7. decoder將解碼之後的視頻填充到輸出緩衝區
  8. decoder釋放輸出緩衝區的同時,將緩衝區中資料渲染到surface

音訊播放類似,只多了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 {        @Override        public 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 while            videoCodec.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;    }

音訊部分類似,完整源碼請移步stefanJi/MediaPlaySimpleDemo

Android媒體解碼MediaCodec MediaExtractor學習

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.