本篇項目地址,名字是媒體解碼MediaCodec,MediaExtractor,求starhttps://github.com/979451341/Audio-and-video-learning-materials這次要用到新的東西SurfaceView、MediaCodec、MediaExtra ...
本篇項目地址,名字是媒體解碼MediaCodec,MediaExtractor,求star
https://github.com/979451341/Audio-and-video-learning-materials
這次要用到新的東西SurfaceView、MediaCodec、MediaExtractor、MediaFormat
1.文字說明
SurfaceView:一個View,用來顯示視頻的,使用的時候代碼都比較簡單就不多說了
MediaCodec:訪問底層媒體編碼,能夠完成媒體編碼和解碼
編碼是將源對象內容按照一種標準轉換為一種標準格式內容。
解碼是和編碼對應的,它使用和編碼相同的標准將編碼內容還原為最初的對象內容。
MediaExtractor:負責將指定類型的媒體文件從文件中找到軌道,並填充到MediaCodec的緩衝區中
MediaFormat:封裝描述媒體數據格式的信息,無論是音頻還是視頻。媒體數據的格式被指定為字元串/值對。所有格式通用的鍵,所有未標記為可選的鍵都是必需的:
名稱 值類型 描述
KEY_MIME 串 格式的類型。
KEY_MAX_INPUT_SIZE 整數 可選,輸入數據緩衝區的最大大小
KEY_BIT_RATE 整數 僅編碼器,所需比特率(以比特/秒為單位)
2.視頻播放順序
(1)開啟兩個線程分別處理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);
設置解碼配置,並給MediaCodec配置,而且將SurfaceView於MediaCodec相關聯,設置為在這個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(); }
迴圈從MediaExtractor取數據放入MediaCodec,同時MediaCodec返回數據,表示視頻播放狀態,然後對應轉檯做不同的處理,
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
然後是音頻,很相似
MediaExtractor獲取資源,選擇頻道,得到MediaFormat,獲取一些參數來配置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);
其實這做的不太好,因為音頻和視頻沒有做協調,之前可能不同步
完整代碼下載地址在文章頭部
參考文章:
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