Android音視頻開發(fā)-入門(四):使用 MediaExtractor 和 MediaMuxer API 解析和封裝 mp4 文件

好時(shí)光

春分已過苞氮,下個(gè)時(shí)節(jié)就是清明了凯力,送上古人小詩一首:

詠柳

【作者】賀知章 【朝代】唐

碧玉妝成一樹高盼产,萬條垂下綠絲絳饵婆。

不知細(xì)葉誰裁出,二月春風(fēng)似剪刀戏售。

MediaExtractor 抽取視頻不含音頻

 /**
     * 抽惹群恕(ly.mp4)的視頻文件不含音頻
     */
    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
    private void extracMedia() {
        MediaExtractor mMediaExtractor = new MediaExtractor();
        try {
            mMediaExtractor.setDataSource(MEDIA_VIDEO_PATH);
            //獲取通道的個(gè)數(shù)
            int trackCount = mMediaExtractor.getTrackCount();
            int videoIndex = -1;
            for (int i = 0; i < trackCount; i++) {
                //這個(gè)trackFormat可不得了可以獲取視頻的寬高,視頻的通道(音頻視頻)灌灾,獲取視頻的長搓译。有的人用這個(gè)獲取幀率我試過,有的手機(jī)行有的
                //不行紧卒,會報(bào)錯侥衬。這是個(gè)坑诗祸,所以還是另求方式
                MediaFormat trackFormat = mMediaExtractor.getTrackFormat(i);
                String typeMimb = trackFormat.getString(MediaFormat.KEY_MIME);
                if (typeMimb.startsWith("video/")) {
                    //這就獲取了音頻的信號通道了
                    videoIndex = i;
                    break;
                }
            }
            //設(shè)置音頻通道信號
            mMediaExtractor.selectTrack(videoIndex);
            //再次拿到這個(gè)視頻通道的format
            MediaFormat trackFormat = mMediaExtractor.getTrackFormat(videoIndex);
            //初始化視頻合成器
            MediaMuxer mediaMuxer = new MediaMuxer(BASE_PATH + "/out_ly.mp4", MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
            //添加給合成器的通道
            mediaMuxer.addTrack(trackFormat);
            ByteBuffer allocate = ByteBuffer.allocate(500 * 1024);

            mediaMuxer.start();
            //獲取視頻的幀率
            long videoSametime = 0;
            mMediaExtractor.readSampleData(allocate, 0);
            //跳過I幀跑芳,要P幀(視頻是由個(gè)別I幀和很多P幀組成)
            if (mMediaExtractor.getSampleFlags() == MediaExtractor.SAMPLE_FLAG_SYNC) {
                mMediaExtractor.advance();
            }
            mMediaExtractor.readSampleData(allocate, 0);
            long firstTime = mMediaExtractor.getSampleTime();
            //下一幀
            mMediaExtractor.advance();

            mMediaExtractor.readSampleData(allocate, 0);
            long senondTime = mMediaExtractor.getSampleTime();
            videoSametime = Math.abs(senondTime - firstTime);
            //重新設(shè)置通道讀取文件
            mMediaExtractor.unselectTrack(videoIndex);
            mMediaExtractor.selectTrack(videoIndex);
            MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
            while (true) {
                int readSamSize = mMediaExtractor.readSampleData(allocate, 0);
                if (readSamSize < 0) {
                    break;
                }
                mMediaExtractor.advance();
                bufferInfo.flags = mMediaExtractor.getSampleFlags();
                bufferInfo.size = readSamSize;
                bufferInfo.offset = 0;
                bufferInfo.presentationTimeUs += videoSametime;
                mediaMuxer.writeSampleData(videoIndex, allocate, bufferInfo);
            }
            mediaMuxer.stop();
            mediaMuxer.release();
            mMediaExtractor.release();
            Log.e("av", "success!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

MediaExtractor 抽取音頻不含視頻

@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
    private void extracAudio() {
        MediaExtractor mediaExtractor = new MediaExtractor();
        try {
            mediaExtractor.setDataSource(AUDIO_VIDEO_PATH);
            int trackCount = mediaExtractor.getTrackCount();
            int audioIndex = -1;
            for (int i = 0; i < trackCount; i++) {
                MediaFormat trackFormat = mediaExtractor.getTrackFormat(i);
                String type = trackFormat.getString(MediaFormat.KEY_MIME);
                if (type.startsWith("audio/")) {
                    audioIndex = i;
                }
            }
            mediaExtractor.selectTrack(audioIndex);
            MediaFormat trackFormat = mediaExtractor.getTrackFormat(audioIndex);
            MediaMuxer mediaMuxer = new MediaMuxer(BASE_PATH + "/out_eat", MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
            int writeAudio = mediaMuxer.addTrack(trackFormat);
            mediaMuxer.start();
            ByteBuffer buffer = ByteBuffer.allocate(1024 * 500);
            MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
            long sampleTime = 0;
            mediaExtractor.readSampleData(buffer, 0);
            if (mediaExtractor.getSampleFlags() == MediaExtractor.SAMPLE_FLAG_SYNC) {
                mediaExtractor.advance();
            }
            mediaExtractor.readSampleData(buffer, 0);
            long firstTime = mediaExtractor.getSampleTime();
            mediaExtractor.advance();
//            mediaExtractor.readSampleData(buffer, 0);
            long secondeTime = mediaExtractor.getSampleTime();
            sampleTime = Math.abs(secondeTime - firstTime);

            mediaExtractor.unselectTrack(audioIndex);
            mediaExtractor.selectTrack(audioIndex);
            while (true) {
                int readSize = mediaExtractor.readSampleData(buffer, 0);
                if (readSize < 0) {
                    break;
                }
//                mediaExtractor.readSampleData(buffer, 0);
                mediaExtractor.advance();
                bufferInfo.size = readSize;
                bufferInfo.flags = mediaExtractor.getSampleFlags();
                bufferInfo.offset = 0;
                bufferInfo.presentationTimeUs = +sampleTime;
                mediaMuxer.writeSampleData(writeAudio, buffer, bufferInfo);
            }
            mediaMuxer.stop();
            mediaMuxer.release();
            mediaExtractor.release();
            Log.e("av", "success");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

MediaExtractor 抽取視頻音頻文件,然后利用MediaMuxer合成

 @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
    private void combineVideo() {
        //用生成的out_ly.map4 和音頻out_eat 合成一個(gè)新的eat_ly.mp4
        try {
            MediaExtractor videoExtractor = new MediaExtractor();
            videoExtractor.setDataSource(BASE_PATH + "/out_ly.mp4");
            MediaFormat videoFormat = null;
            int videoTrackIndex = -1;
            int videoTrackCount = videoExtractor.getTrackCount();
            for (int i = 0; i < videoTrackCount; i++) {
                videoFormat = videoExtractor.getTrackFormat(i);
                String mimeType = videoFormat.getString(MediaFormat.KEY_MIME);
                if (mimeType.startsWith("video/")) {
                    videoTrackIndex = i;
                    break;
                }
            }

            MediaExtractor audioExtractor = new MediaExtractor();
            audioExtractor.setDataSource(BASE_PATH + "/out_eat");
            MediaFormat audioFormat = null;
            int audioTrackIndex = -1;
            int audioTrackCount = audioExtractor.getTrackCount();
            for (int i = 0; i < audioTrackCount; i++) {
                audioFormat = audioExtractor.getTrackFormat(i);
                String mimeType = audioFormat.getString(MediaFormat.KEY_MIME);
                if (mimeType.startsWith("audio/")) {
                    audioTrackIndex = i;
                    break;
                }
            }

            videoExtractor.selectTrack(videoTrackIndex);
            audioExtractor.selectTrack(audioTrackIndex);

            MediaCodec.BufferInfo videoBufferInfo = new MediaCodec.BufferInfo();
            MediaCodec.BufferInfo audioBufferInfo = new MediaCodec.BufferInfo();

            MediaMuxer mediaMuxer = new MediaMuxer(BASE_PATH + "/eat_ly.mp4", MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
            int writeVideoTrackIndex = mediaMuxer.addTrack(videoFormat);
            int writeAudioTrackIndex = mediaMuxer.addTrack(audioFormat);
            mediaMuxer.start();

            ByteBuffer byteBuffer = ByteBuffer.allocate(500 * 1024);
            long sampleTime = 0;
            {
                videoExtractor.readSampleData(byteBuffer, 0);
                if (videoExtractor.getSampleFlags() == MediaExtractor.SAMPLE_FLAG_SYNC) {
                    videoExtractor.advance();
                }
                videoExtractor.readSampleData(byteBuffer, 0);
                long secondTime = videoExtractor.getSampleTime();
                videoExtractor.advance();
                long thirdTime = videoExtractor.getSampleTime();
                sampleTime = Math.abs(thirdTime - secondTime);
            }
            videoExtractor.unselectTrack(videoTrackIndex);
            videoExtractor.selectTrack(videoTrackIndex);



            long stampTimeAudio = 0;
            ByteBuffer byteBufferAudio = ByteBuffer.allocate(500 * 1024);
            //獲取幀之間的間隔時(shí)間
            {
                audioExtractor.readSampleData(byteBufferAudio, 0);
                if (audioExtractor.getSampleFlags() == MediaExtractor.SAMPLE_FLAG_SYNC) {
                    audioExtractor.advance();
                }
                audioExtractor.readSampleData(byteBufferAudio, 0);
                long secondTime = audioExtractor.getSampleTime();
                audioExtractor.advance();
                long thirdTime = audioExtractor.getSampleTime();
                stampTimeAudio = Math.abs(thirdTime - secondTime);
                Log.e("fuck", stampTimeAudio + "");
            }

            audioExtractor.unselectTrack(audioTrackIndex);
            audioExtractor.selectTrack(audioTrackIndex);



            while (true) {
                int readVideoSampleSize = videoExtractor.readSampleData(byteBuffer, 0);
                if (readVideoSampleSize < 0) {
                    break;
                }
                videoBufferInfo.size = readVideoSampleSize;
                videoBufferInfo.presentationTimeUs += sampleTime;
                videoBufferInfo.offset = 0;
                videoBufferInfo.flags = videoExtractor.getSampleFlags();
                mediaMuxer.writeSampleData(writeVideoTrackIndex, byteBuffer, videoBufferInfo);
                videoExtractor.advance();
            }

            while (true) {
                int readAudioSampleSize = audioExtractor.readSampleData(byteBufferAudio, 0);
                if (readAudioSampleSize < 0) {
                    break;
                }

                audioBufferInfo.size = readAudioSampleSize;
                audioBufferInfo.presentationTimeUs += stampTimeAudio;
                audioBufferInfo.offset = 0;
                audioBufferInfo.flags = audioExtractor.getSampleFlags();
                mediaMuxer.writeSampleData(writeAudioTrackIndex, byteBufferAudio, audioBufferInfo);
                audioExtractor.advance();
            }

            mediaMuxer.stop();
            mediaMuxer.release();
            videoExtractor.release();
            audioExtractor.release();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

以上代碼全部測試通過直颅,歡迎交流2└觥!功偿!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末盆佣,一起剝皮案震驚了整個(gè)濱河市往堡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌共耍,老刑警劉巖虑灰,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異痹兜,居然都是意外死亡穆咐,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進(jìn)店門字旭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來对湃,“玉大人,你說我怎么就攤上這事遗淳∨钠猓” “怎么了?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵屈暗,是天一觀的道長拆讯。 經(jīng)常有香客問我,道長养叛,這世上最難降的妖魔是什么往果? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮一铅,結(jié)果婚禮上陕贮,老公的妹妹穿的比我還像新娘。我一直安慰自己潘飘,他們只是感情好肮之,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著卜录,像睡著了一般戈擒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上艰毒,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天筐高,我揣著相機(jī)與錄音,去河邊找鬼丑瞧。 笑死柑土,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的绊汹。 我是一名探鬼主播稽屏,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼西乖!你這毒婦竟也來了狐榔?” 一聲冷哼從身側(cè)響起坛增,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎薄腻,沒想到半個(gè)月后收捣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡庵楷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年坏晦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嫁乘。...
    茶點(diǎn)故事閱讀 40,498評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡昆婿,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蜓斧,到底是詐尸還是另有隱情仓蛆,我是刑警寧澤,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布挎春,位于F島的核電站看疙,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏直奋。R本人自食惡果不足惜能庆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望脚线。 院中可真熱鬧搁胆,春花似錦、人聲如沸邮绿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽船逮。三九已至顾腊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間挖胃,已是汗流浹背杂靶。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留酱鸭,地道東北人吗垮。 一個(gè)月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像凛辣,于是被迫代替她去往敵國和親抱既。 傳聞我的和親對象是個(gè)殘疾皇子职烧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 赤壁賦 壬戌之秋扁誓,七月既望防泵,蘇子與客泛舟游于赤壁之下。清風(fēng)徐來蝗敢,水波不興捷泞。舉酒屬客,誦明月之詩寿谴,歌窈窕之章锁右。少焉,...
    HannyYeung閱讀 20,977評論 6 60
  • 主講老師——忘機(jī) 2017.9.3 竹韻會員群絕句講座第一課導(dǎo)師:落日...
    只若閱讀 2,095評論 2 9
  • 竹韻會員群絕句講座第一課導(dǎo)師:落日長河讶泰,無語主講:忘機(jī)策劃: 蒹葭作業(yè)評審:一公升眼淚咏瑟,相看不厭,劉春志, 未老...
    舞蝶兒閱讀 2,752評論 0 3
  • 前言 隨著音視頻領(lǐng)域的火熱痪署,在很多領(lǐng)域(教育码泞,游戲,娛樂狼犯,體育余寥,跑步,餐飲悯森,音樂等)嘗試做音視頻直播/點(diǎn)播功能宋舷,那...
    passiontim閱讀 3,293評論 1 46
  • 自假期開始 便陷入了混沌世界 必做的貌似做好了卻又沒做好 該玩放肆的 似乎盡興卻又未盡興 譬如今夜 在很長的一段間...
    胡為哉miaow閱讀 559評論 0 0