1. 程式人生 > >Android-音視訊(6):用 MediaExtractor 和 MediaMuxer API 解析和封裝 mp4 檔案

Android-音視訊(6):用 MediaExtractor 和 MediaMuxer API 解析和封裝 mp4 檔案

1.MediaExtractor API的作用

作用:

  • 可以把音視訊檔案的音訊和視訊分離,並抽取相應的資料通道,然後進行操作。

如何使用:

  1. 先要知道是針對哪個檔案操作,所以要用 setDataSource(String filePath) 設定目標檔案。
  2. 然後需要知道這個檔案所有的通道數,用 getTrackCount() 得到,通過遍歷,得到需要的通道 int i。
  3. 根據得到的通道,用 getTrackFirmat(int i ) 得到這個通道的資料格式(MediaFormat mediaFormat)。
  4. 然後把MediaExtractor對準這個通道 用 selectTack(int i),準備讀取資料。
  5. readSampleData(ByteBuffer byteBuffer , int offset) 把指定通道中的資料按偏移量讀取到byteBuffer中,這只是一幀的資料。
  6. 有了這個byteBuffer資料,之後的事就交給MediaMuxer去操作這一幀的資料,之後呼叫 adVance()調取下一幀,重複5、6。
  7. 操作完之後,記得釋放 release()。

程式碼示例將在下面和MediaMuxer一起給出。

2.MediaMuxer API的作用

作用:

  • 生成一個音訊或視訊檔案;還可以把音訊與視訊混合成一個音視訊檔案

如何使用:

  1. 因為是生成一個檔案,所以構造一個MediaMuxer的時候需要傳入檔案的路徑,和檔案的格式如:new MediaMuxer(String newfilepath, int format)  格式一般為: MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4
  2. 然後需要新增通道,記錄一個數據通道的格式(MediaExtracktor第3步得到) addTrack(MediaFormat format)並得到一個trackIndex,之後用這個判斷用哪個通道寫入資料。
  3. start():開始合成檔案
  4. 每當MediaExtracktor的第5步後,用 writeSampleData(int trackIndex, ByteBuffer byteBuf, MediaCodec.BufferInfo bufferInfo):把ByteBuffer中的資料寫入之前設定的檔案中。
  5. 資料寫入完成,stop():停止合成檔案   release():釋放資源

3.結合的示例程式碼如下:

String mp4FilePath = Environment.getExternalStorageDirectory().getAbsolutePath()+ File.separator+"test.mp4";
String newVideoFilePath = Environment.getExternalStorageDirectory().getAbsolutePath()+File.separator+"testVideo.mp4";

private void startGetVideo() { //抽取mp4檔案的視訊資料,並生成新的mp4檔案,但是沒有聲音。
        File videofile = new File(newVideoFilePath);
        if (videofile.exists()){
            videofile.delete();
            try {
                videofile.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        getVideoThread = new Thread(){
            @Override
            public void run() {
                try {
                    mediaExtractor = new MediaExtractor();
                    //設定視訊檔案,用於提取視訊軌道資料
                    mediaExtractor.setDataSource(mp4FilePath);
                    int videoTrackIndex = -1;
                    int framerate = 0;
                    for (int i = 0 ; i < mediaExtractor.getTrackCount() ; i++){
                        MediaFormat mediaformat = mediaExtractor.getTrackFormat(i);
                        //跳過不是視訊軌道的軌道
                        if (!(mediaformat.getString(MediaFormat.KEY_MIME)).startsWith("video/")){
                            continue;
                        }
                        //幾幀/秒
                        framerate = mediaformat.getInteger(MediaFormat.KEY_FRAME_RATE);
                        //選擇視訊軌道
                        mediaExtractor.selectTrack(i);
                        //設定視訊軌道的輸出位置和格式
                        mediaMuxer = new MediaMuxer(newVideoFilePath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
                        //記錄視訊軌道值,之後要用這個值判斷寫入資料
                        videoTrackIndex = mediaMuxer.addTrack(mediaformat);
                        mediaMuxer.start();
                    }
                    if (mediaMuxer == null){
                        return;
                    }
                    //讀取軌道資訊時,以及向新檔案寫入軌道資訊時。用於儲存每一幀的資訊(如時長,size,flag)
                    MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
                    //當前演示時長 初始化為0
                    info.presentationTimeUs = 0;
                    int sampleSize = 0;
                    //申請一個空間儲存每一幀視訊資料,然後寫入新檔案
                    ByteBuffer buffer = ByteBuffer.allocate(500*1024);
                    while((sampleSize = mediaExtractor.readSampleData(buffer,0)) > 0){
                        //儲存幀資訊
                        info.size = sampleSize;
                        info.flags = MediaCodec.BUFFER_FLAG_SYNC_FRAME;
                        info.presentationTimeUs += 1000*1000 / framerate; // (1000*1000 / framerate) 是一幀的微秒時長
                        //儲存幀資料(向指定的視訊軌道寫入)
                        mediaMuxer.writeSampleData(videoTrackIndex,buffer,info);
                        mediaExtractor.advance(); //下一幀
                    }
                    mediaExtractor.release();
                    mediaMuxer.stop();
                    mediaMuxer.release();
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            showInfor.append("\n"+"testVideo.mp4 視訊軌道資料提取成功");
                        }
                    });
                } catch (IOException e) {
                    e.printStackTrace();
                }finally {
                    getVideoThread = null;
                }
            }
        };
        getVideoThread.start();
    }