溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Android 音視頻深入 三 MP4解碼播放視頻 (附源碼下載)

發(fā)布時(shí)間:2020-09-04 15:36:41 來源:網(wǎng)絡(luò) 閱讀:680 作者:qq5a70561b29238 欄目:移動(dòng)開發(fā)

本篇項(xiàng)目地址,名字是媒體解碼MediaCodec,MediaExtractor,求star
https://github.com/979451341/Audio-and-video-learning-materials

這次要用到新的東西SurfaceView、MediaCodec、MediaExtractor、MediaFormat

1.文字說明

SurfaceView:一個(gè)View,用來顯示視頻的,使用的時(shí)候代碼都比較簡單就不多說了

MediaCodec:訪問底層媒體編碼,能夠完成媒體編碼和解碼
編碼是將源對象內(nèi)容按照一種標(biāo)準(zhǔn)轉(zhuǎn)換為一種標(biāo)準(zhǔn)格式內(nèi)容。
解碼是和編碼對應(yīng)的,它使用和編碼相同的標(biāo)準(zhǔn)將編碼內(nèi)容還原為最初的對象內(nèi)容。

MediaExtractor:負(fù)責(zé)將指定類型的媒體文件從文件中找到軌道,并填充到MediaCodec的緩沖區(qū)中

MediaFormat:封裝描述媒體數(shù)據(jù)格式的信息,無論是音頻還是視頻。媒體數(shù)據(jù)的格式被指定為字符串/值對。所有格式通用的鍵,所有未標(biāo)記為可選的鍵都是必需的:
名稱 值類型 描述
KEY_MIME 串 格式的類型。
KEY_MAX_INPUT_SIZE 整數(shù) 可選,輸入數(shù)據(jù)緩沖區(qū)的最大大小
KEY_BIT_RATE 整數(shù) 僅編碼器,所需比特率(以比特/秒為單位)

2.視頻播放順序

(1)開啟兩個(gè)線程分別處理MP4的音頻和視頻

我先說一下視頻的處理

使用MediaExtractor提取資源,選擇頻道

        MediaExtractor videoExtractor = new MediaExtractor();
        MediaCodec videoCodec = null;
        try {
            videoExtractor.setDataSource(filePath);
        } catch (IOException e) {
            e.printStackTrace();
        }
        int videoTrackIndex;
        //獲取視頻所在軌道
        videoTrackIndex = getMediaTrackIndex(videoExtractor, "video/");

videoExtractor.selectTrack(videoTrackIndex);

設(shè)置解碼配置,并給MediaCodec配置,而且將SurfaceView于MediaCodec相關(guān)聯(lián),設(shè)置為在這個(gè)SurfaceView上顯示

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

循環(huán)從MediaExtractor取數(shù)據(jù)放入MediaCodec,同時(shí)MediaCodec返回?cái)?shù)據(jù),表示視頻播放狀態(tài),然后對應(yīng)轉(zhuǎn)臺(tái)做不同的處理,

        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, "超時(shí)");
                    break;
                case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
                    //outputBuffers = videoCodec.getOutputBuffers();
                    Log.v(TAG, "output buffers changed");
                    break;
                default:
                    //直接渲染到Surface時(shí)使用不到outputBuffer
                    //ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
                    //延時(shí)操作
                    //如果緩沖區(qū)里的可展示時(shí)間>當(dāng)前視頻播放的進(jìn)度,就休眠一下
                    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

然后是音頻,很相似
MediaExtractor獲取資源,選擇頻道,得到MediaFormat,獲取一些參數(shù)來配置AudioTrack,然后就不說了,上一篇博客說了如何播放音頻,

        MediaExtractor audioExtractor = new MediaExtractor();
        try {
            audioExtractor.setDataSource(filePath);
        } catch (IOException e) {
            e.printStackTrace();
        }

           MediaFormat mediaFormat = audioExtractor.getTrackFormat(i);
            String mime = mediaFormat.getString(MediaFormat.KEY_MIME);
            if (mime.startsWith("audio/")) {
                audioExtractor.selectTrack(i);

                int audioChannels = mediaFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
                int audioSampleRate = mediaFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE);
                int minBufferSize = AudioTrack.getMinBufferSize(audioSampleRate,
                        (audioChannels == 1 ? AudioFormat.CHANNEL_OUT_MONO : AudioFormat.CHANNEL_OUT_STEREO),
                        AudioFormat.ENCODING_PCM_16BIT);
                int maxInputSize = mediaFormat.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE);

                    audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
                        audioSampleRate,
                        (audioChannels == 1 ? AudioFormat.CHANNEL_OUT_MONO : AudioFormat.CHANNEL_OUT_STEREO),
                        AudioFormat.ENCODING_PCM_16BIT,
                        audioInputBufferSize,
                        AudioTrack.MODE_STREAM);

其實(shí)這做的不太好,因?yàn)橐纛l和視頻沒有做協(xié)調(diào),之前可能不同步

完整代碼下載地址在文章頭部

參考文章:
http://blog.csdn.net/qq_36467463/article/details/77990089
https://www.cnblogs.com/jiy-for-you/p/7282033.html
https://www.jianshu.com/p/6df2ab17651a

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI