MediaExtractor的使用

分离mp4、flv,生成视频H264/mpeg和音频mp3或aac(无adts头)

下面是应用层mediaExtractor使用,分离一mp4文件,生成h264和aac,并添加adts头文件。


import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.util.Log;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;

/**
 * mp4extractor 分离器
 * 分离mp4 成h264 和 aac
 * 分别生成h264和aac文件(无adts头)
 * <p>
 * android的无法分离高profile的h264文件,这里分离的是Baseline(profile)
 * <p>
 * 合成的时候,MP4,flv,rtmp都不需要adts头
 * 合成的时候:hls,rtp,ts需要adts头
 * 所以这里分离出来的aac是没有adts头的,mediainfo读取到的音频信息是从moov中box中读取的
 */


public class MP4VideoExtractor {
    private static final String TAG = "MP4VideoExtractor";


    //test3.mp4  h264,aac
    public static void exactorMedia(String sdcard_path) {
        FileOutputStream videoOutputStream = null;
        FileOutputStream audioOutputStream = null;
        MediaExtractor mediaExtractor = new MediaExtractor();
        try {
            //分离的视频文件
            File videoFile = new File(sdcard_path, "output_video.h264");
            //分离的音频文件
            File audioFile = new File(sdcard_path, "output_audio.aac");
            videoOutputStream = new FileOutputStream(videoFile);
            audioOutputStream = new FileOutputStream(audioFile);
            //输入文件,也可以是网络文件
            //oxford.mp4 视频 h264/baseline  音频 aac/lc 44.1k  2 channel 128kb/s
            mediaExtractor.setDataSource(sdcard_path + "/oxford.mp4");
            //test3.mp4  视频h264 high   音频aac
            //        mediaExtractor.setDataSource(sdcard_path + "/test3.mp4");
            //test2.mp4 视频mpeg4  音频MP3
            //  mediaExtractor.setDataSource(sdcard_path + "/test2.mp4");
            //信道总数
            int trackCount = mediaExtractor.getTrackCount();
            Log.d(TAG, "trackCount:" + trackCount);
            int audioTrackIndex = -1;
            int videoTrackIndex = -1;
            for (int i = 0; i < trackCount; i++) {
                MediaFormat trackFormat = mediaExtractor.getTrackFormat(i);
                String mineType = trackFormat.getString(MediaFormat.KEY_MIME);
                //视频信道
                if (mineType.startsWith("video/")) {
                    videoTrackIndex = i;
                }
                //音频信道
                if (mineType.startsWith("audio/")) {
                    audioTrackIndex = i;
                }
            }

            ByteBuffer byteBuffer = ByteBuffer.allocate(500 * 1024);
            //切换到视频信道
            mediaExtractor.selectTrack(videoTrackIndex);
            while (true) {
                int readSampleCount = mediaExtractor.readSampleData(byteBuffer, 0);
                Log.d(TAG, "video:readSampleCount:" + readSampleCount);
                if (readSampleCount < 0) {
                    break;
                }
                //保存视频信道信息
                byte[] buffer = new byte[readSampleCount];
                byteBuffer.get(buffer);
                videoOutputStream.write(buffer);//buffer 写入到 videooutputstream中
                byteBuffer.clear();
                mediaExtractor.advance();
            }
            //切换到音频信道
            mediaExtractor.selectTrack(audioTrackIndex);
            while (true) {
                int readSampleCount = mediaExtractor.readSampleData(byteBuffer, 0);
                Log.d(TAG, "audio:readSampleCount:" + readSampleCount);
                if (readSampleCount < 0) {
                    break;
                }
                //保存音频信息
                byte[] buffer = new byte[readSampleCount];
                byteBuffer.get(buffer);
                /************************* 用来为aac添加adts头**************************/
                byte[] aacaudiobuffer = new byte[readSampleCount + 7];
                addADTStoPacket(aacaudiobuffer, readSampleCount + 7);
                System.arraycopy(buffer, 0, aacaudiobuffer, 7, readSampleCount);
                audioOutputStream.write(aacaudiobuffer);
                /***************************************close**************************/
                //  audioOutputStream.write(buffer);
                byteBuffer.clear();
                mediaExtractor.advance();
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            Log.d(TAG, "mediaExtractor.release!\n");
            mediaExtractor.release();
            mediaExtractor = null;
            try {
                videoOutputStream.close();
                audioOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /** 这里之前遇到一个坑,以为这个packetLen是adts头的长度,也就是7,仔细看了下代码,发现这个不是adts头的长度,而是一帧音频的长度
     * @param packet    一帧数据(包含adts头长度)
     * @param packetLen 一帧数据(包含adts头)的长度
     */
    private static void addADTStoPacket(byte[] packet, int packetLen) {
        int profile = 2; // AAC LC
        int freqIdx = getFreqIdx(44100);
        int chanCfg = 2; // CPE

        // fill in ADTS data
        packet[0] = (byte) 0xFF;
        packet[1] = (byte) 0xF9;
        packet[2] = (byte) (((profile - 1) << 6) + (freqIdx << 2) + (chanCfg >> 2));
        packet[3] = (byte) (((chanCfg & 3) << 6) + (packetLen >> 11));
        packet[4] = (byte) ((packetLen & 0x7FF) >> 3);
        packet[5] = (byte) (((packetLen & 7) << 5) + 0x1F);
        packet[6] = (byte) 0xFC;
    }


    private static int getFreqIdx(int sampleRate) {
        int freqIdx;

        switch (sampleRate) {
            case 96000:
                freqIdx = 0;
                break;
            case 88200:
                freqIdx = 1;
                break;
            case 64000:
                freqIdx = 2;
                break;
            case 48000:
                freqIdx = 3;
                break;
            case 44100:
                freqIdx = 4;
                break;
            case 32000:
                freqIdx = 5;
                break;
            case 24000:
                freqIdx = 6;
                break;
            case 22050:
                freqIdx = 7;
                break;
            case 16000:
                freqIdx = 8;
                break;
            case 12000:
                freqIdx = 9;
                break;
            case 11025:
                freqIdx = 10;
                break;
            case 8000:
                freqIdx = 11;
                break;
            case 7350:
                freqIdx = 12;
                break;
            default:
                freqIdx = 8;
                break;
        }

        return freqIdx;
    }

}
发布了34 篇原创文章 · 获赞 14 · 访问量 4万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 深蓝海洋 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览