實現把一個音頻文件的視頻抽取出來

前言

一個音視頻文件是由音頻和視頻組成的;我們可以通過MediaExtractor、MediaMuxer把音頻或視頻給單獨抽取出來;抽取出來的音頻和視頻能單獨播放;

知識結構

MediaExtractor :作用是把音頻和視頻的數據進行分離;
它常用的方法有:

1、setDataSource(String path):
即可以設置本地文件又可以設置網絡文件
Sets the data source (file-path or http URL) to use

2、getTrackCount():
得到源文件通道數
Count the number of tracks found in the data source.

3、getTrackFormat(int index):獲取指定(index)的通道格式
Get the track format at the specified index.

4、getSampleTime():返回當前的時間戳
Returns the current sample’s presentation time in microseconds.

5、readSampleData(ByteBuffer byteBuf, int offset):把指定通道中的數據按偏移量讀取到ByteBuffer中;
Retrieve the current encoded sample and store it in the byte buffer starting at the given offset.

6、advance():讀取下一幀數據
Advance to the next sample.

7、release():讀取結束後,釋放資源

MediaMuxer:生成音頻或視頻文件;還可以把音頻與視頻混合成一個音視頻文件;
它常用的方法:

1、MediaMuxer(String path, int format):
path:輸出文件的名稱
format:輸出文件的格式;當前只支持MP4格式;

2、addTrack(MediaFormat format):添加通道;我們更多的是使用MediaCodec.getOutpurForma()或Extractor.getTrackFormat(int index)來獲取MediaFormat;也可以自己創建;
Adds a track with the specified format.

3、start():開始合成文件
Starts the muxer.

4、writeSampleData(int trackIndex, ByteBuffer byteBuf, MediaCodec.BufferInfo bufferInfo):把ByteBuffer中的數據寫入到在構造器設置的文件中;
Writes an encoded sample into the muxer.

5、stop():停止合成文件
Stops the muxer.

6、release():釋放資源
Make sure you call this when you're done to free up any resources instead of relying on the garbage collector to do this for you at some point in the future.

僞代碼

MediaExtractor僞代碼

 MediaExtractor extractor = new MediaExtractor();
 extractor.setDataSource(...);
 int numTracks = extractor.getTrackCount();
 for (int i = 0; i < numTracks; ++i) {
   MediaFormat format = extractor.getTrackFormat(i);
   String mime = format.getString(MediaFormat.KEY_MIME);
   if (weAreInterestedInThisTrack) {
     extractor.selectTrack(i);
   }
 }
 ByteBuffer inputBuffer = ByteBuffer.allocate(...)
 while (extractor.readSampleData(inputBuffer, ...) >= 0) {
   int trackIndex = extractor.getSampleTrackIndex();
   long presentationTimeUs = extractor.getSampleTime();
   ...
   extractor.advance();
 }

 extractor.release();
 extractor = null;

MediaMuxer僞代碼:

MediaMuxer muxer = new MediaMuxer("temp.mp4", OutputFormat.MUXER_OUTPUT_MPEG_4);
 // More often, the MediaFormat will be retrieved from MediaCodec.getOutputFormat()
 // or MediaExtractor.getTrackFormat().
 MediaFormat audioFormat = new MediaFormat(...);
 MediaFormat videoFormat = new MediaFormat(...);
 int audioTrackIndex = muxer.addTrack(audioFormat);
 int videoTrackIndex = muxer.addTrack(videoFormat);
 ByteBuffer inputBuffer = ByteBuffer.allocate(bufferSize);
 boolean finished = false;
 BufferInfo bufferInfo = new BufferInfo();

 muxer.start();
 while(!finished) {
   // getInputBuffer() will fill the inputBuffer with one frame of encoded
   // sample from either MediaCodec or MediaExtractor, set isAudioSample to
   // true when the sample is audio data, set up all the fields of bufferInfo,
   // and return true if there are no more samples.
   finished = getInputBuffer(inputBuffer, isAudioSample, bufferInfo);
   if (!finished) {
     int currentTrackIndex = isAudioSample ? audioTrackIndex : videoTrackIndex;
     muxer.writeSampleData(currentTrackIndex, inputBuffer, bufferInfo);
   }
 };
 muxer.stop();
 muxer.release();

示例代碼

package com.example.administrator.mediaextractor_mediamuxer;

import android.media.MediaCodec;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.media.MediaMuxer;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

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

public class MainActivity extends AppCompatActivity {

    private static final String SDCARD_PATH  = Environment.getExternalStorageDirectory().getPath();

    private MediaExtractor mMediaExtractor;
    private MediaMuxer mMediaMuxer;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    process();
                }
                catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    private boolean process() throws IOException {
        mMediaExtractor = new MediaExtractor();
        mMediaExtractor.setDataSource(SDCARD_PATH+"/input.mp4");

        int mVideoTrackIndex = -1;
        int framerate = 0;
        for(int i = 0; i < mMediaExtractor.getTrackCount(); i++) {
            MediaFormat format = mMediaExtractor.getTrackFormat(i);
            String mime = format.getString(MediaFormat.KEY_MIME);
            if(!mime.startsWith("video/")) {
                continue;
            }
            framerate = format.getInteger(MediaFormat.KEY_FRAME_RATE);
            mMediaExtractor.selectTrack(i);
            mMediaMuxer = new MediaMuxer(SDCARD_PATH+"/ouput.mp4", MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
            mVideoTrackIndex = mMediaMuxer.addTrack(format);
            mMediaMuxer.start();
        }

        if(mMediaMuxer == null) {
            return false;
        }

        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
        info.presentationTimeUs = 0;
        ByteBuffer buffer = ByteBuffer.allocate(500*1024);
        int sampleSize = 0;
        while((sampleSize = mMediaExtractor.readSampleData(buffer, 0)) > 0) {

            info.offset = 0;
            info.size = sampleSize;
            info.flags = MediaCodec.BUFFER_FLAG_SYNC_FRAME;
            info.presentationTimeUs += 1000*1000/framerate;
            mMediaMuxer.writeSampleData(mVideoTrackIndex,buffer,info);
            mMediaExtractor.advance();
        }

        mMediaExtractor.release();

        mMediaMuxer.stop();
        mMediaMuxer.release();

        return true;
    }

}

下載項目MediaExtractor抽取視頻->MediaMuxer產生視頻文件

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章