vx 搜索『gjzkeyframe』 關注『關鍵幀Keyframe』來及時獲得最新的音視頻技術文章。
iOS/Android 客戶端開發(fā)同學如果想要開始學習音視頻開發(fā),最絲滑的方式是對音視頻基礎概念知識有一定了解后,再借助 iOS/Android 平臺的音視頻能力上手去實踐音視頻的采集 → 編碼 → 封裝 → 解封裝 → 解碼 → 渲染過程,并借助音視頻工具來分析和理解對應的音視頻數(shù)據(jù)。

iOS/Android 客戶端開發(fā)同學如果想要開始學習音視頻開發(fā),最絲滑的方式是對音視頻基礎概念知識有一定了解后,再借助 iOS/Android 平臺的音視頻能力上手去實踐音視頻的采集 → 編碼 → 封裝 → 解封裝 → 解碼 → 渲染過程,并借助音視頻工具來分析和理解對應的音視頻數(shù)據(jù)。
在音視頻工程示例這個欄目,我們將通過拆解采集 → 編碼 → 封裝 → 解封裝 → 解碼 → 渲染流程并實現(xiàn) Demo 來向大家介紹如何在 iOS/Android 平臺上手音視頻開發(fā)。
這里是 Android 第十一篇:Android 視頻轉封裝 Demo。這個 Demo 里包含以下內容:
- 1)實現(xiàn)一個音視頻解封裝模塊;
- 2)實現(xiàn)一個音視頻封裝模塊;
- 3)實現(xiàn)對 MP4 文件中音視頻的解封裝邏輯,將解封裝后的音視頻編碼數(shù)據(jù)重新封裝存儲為一個新的 MP4 文件;
- 4)詳盡的代碼注釋,幫你理解代碼邏輯和原理。
在本文中,我們將詳解一下 Demo 的具體實現(xiàn)和源碼。讀完本文內容相信就能幫你掌握相關知識。
不過,如果你的需求是:1)直接獲得全部工程源碼;2)想進一步咨詢音視頻技術問題;3)咨詢音視頻職業(yè)發(fā)展問題??梢愿鶕?jù)自己的需要考慮是否加入『關鍵幀的音視頻開發(fā)圈』。
1、音視頻解封裝模塊
視頻編碼模塊即 KFMP4Demuxer,復用了《Android 音頻解封裝 Demo》中介紹的 demuxer,這里就不再重復介紹了,其接口如下:
KFMP4Demuxer.java
public class KFMP4Demuxer {
public KFMP4Demuxer(KFDemuxerConfig config, KFDemuxerListener listener); ///< 構造方法:配置 & 回調。
public void release(); ///< 釋放解封裝器實例。
public boolean hasVideo(); ///< 是否包含視頻。
public boolean hasAudio(); ///< 是否包含音頻。
public int duration(); ///< 文件時長。
public int rotation(); ///< 視頻旋轉角度。
public boolean isHEVC(); ///< 是否為 H265。
public int width(); ///< 視頻寬度。
public int height(); ///< 視頻高度。
public int samplerate(); ///< 音頻采樣率。
public int channel(); ///< 音頻聲道數(shù)。
public int audioProfile(); ///< 音頻 profile。
public int videoProfile(); ///< 視頻 profile。
public MediaFormat audioMediaFormat(); ///< 音頻格式描述。
public MediaFormat videoMediaFormat(); //< 視頻格式描述。
public ByteBuffer readAudioSampleData(MediaCodec.BufferInfo bufferInfo); ///< 讀取音頻幀。
public ByteBuffer readVideoSampleData(MediaCodec.BufferInfo bufferInfo); ///< 讀取視頻幀。
}
2、音視頻封裝模塊
視頻編碼模塊即 KFMP4Muxer,復用了《Android 音頻封裝 Demo》中介紹的 muxer,這里就不再重復介紹了,其接口如下:
KFMP4Muxer.java
public class KFMP4Muxer {
public KFMP4Muxer(KFMuxerConfig config, KFMuxerListener listener); ///< 構造方法,配置、回調。
public void start(); ///< 開始。
public void stop(); ///< 關閉。
public void setVideoMediaFormat(MediaFormat mediaFormat); ///< 設置音頻描述。
public void setAudioMediaFormat(MediaFormat mediaFormat); ///< 設置視頻描述。
public void writeSampleData(boolean isVideo, ByteBuffer buffer, MediaCodec.BufferInfo bufferInfo); ///< 寫入音視頻數(shù)據(jù)(編碼后數(shù)據(jù))。
public void release(); ///< 清理。
}
3、音視頻轉封裝邏輯
我們還是在一個 MainActivity 中來實現(xiàn)對 MP4 文件中音視頻的解封裝邏輯,然后將解封裝后的音視頻編碼數(shù)據(jù)重新封裝存儲為一個新的 MP4 文件。
MainActivity.java
public class MainActivity extends AppCompatActivity {
private KFMP4Demuxer mDemuxer; ///< 解封裝器。
private KFDemuxerConfig mDemuxerConfig; ///< 解封裝配置。
private KFMP4Muxer mMuxer; ///< 封裝器。
private KFMuxerConfig mMuxerConfig; ///< 封裝配置。
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
///< 申請采集存儲權限。
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED ||
ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED ||
ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions((Activity) this,
new String[] {Manifest.permission.CAMERA,Manifest.permission.RECORD_AUDIO, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE},
1);
}
///< 解封裝配置,控制僅輸出視頻。
mDemuxerConfig = new KFDemuxerConfig();
mDemuxerConfig.path = Environment.getExternalStorageDirectory().getPath() + "/2.mp4";
mDemuxerConfig.demuxerType = KFMediaBase.KFMediaType.KFMediaAV;
///< 創(chuàng)建封裝配置。
mMuxerConfig = new KFMuxerConfig(Environment.getExternalStorageDirectory().getPath() + "/test.mp4");
FrameLayout.LayoutParams startParams = new FrameLayout.LayoutParams(200, 120);
startParams.gravity = Gravity.CENTER_HORIZONTAL;
Button startButton = new Button(this);
startButton.setTextColor(Color.BLUE);
startButton.setText("開始");
startButton.setVisibility(View.VISIBLE);
startButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
///< 創(chuàng)建解封裝與封裝。
if (mDemuxer == null) {
mDemuxer = new KFMP4Demuxer(mDemuxerConfig,mDemuxerListener);
mMuxer = new KFMP4Muxer(mMuxerConfig,mMuxerListener);
mMuxer.start();
///< 設置格式描述。
mMuxer.setVideoMediaFormat(mDemuxer.videoMediaFormat());
mMuxer.setAudioMediaFormat(mDemuxer.audioMediaFormat());
///< 循環(huán)讀取音視頻數(shù)據(jù)寫入封裝器。
MediaCodec.BufferInfo videoBufferInfo = new MediaCodec.BufferInfo();
ByteBuffer videoNextBuffer = mDemuxer.readVideoSampleData(videoBufferInfo);
MediaCodec.BufferInfo audioBufferInfo = new MediaCodec.BufferInfo();
ByteBuffer audioNextBuffer = mDemuxer.readAudioSampleData(audioBufferInfo);
while (audioNextBuffer != null || videoNextBuffer != null) {
if (audioNextBuffer != null) {
mMuxer.writeSampleData(false,audioNextBuffer,audioBufferInfo);
audioNextBuffer = mDemuxer.readAudioSampleData(audioBufferInfo);
}
if (videoNextBuffer != null) {
mMuxer.writeSampleData(true,videoNextBuffer,videoBufferInfo);
videoNextBuffer = mDemuxer.readVideoSampleData(videoBufferInfo);
}
}
mMuxer.stop();
Log.i("KFDemuxer","complete");
}
}
});
addContentView(startButton, startParams);
}
private KFDemuxerListener mDemuxerListener = new KFDemuxerListener() {
@Override
///< 解封裝出錯回調。
public void demuxerOnError(int error, String errorMsg) {
Log.i("KFDemuxer","error" + error + "msg" + errorMsg);
}
};
private KFMuxerListener mMuxerListener = new KFMuxerListener() {
@Override
///< 封裝器出錯回調。
public void muxerOnError(int error, String errorMsg) {
Log.e("KFMuxer","error:" + error + "msg:" +errorMsg);
}
};
}
上面是 MainActivity 的實現(xiàn),其中主要包含這幾個部分:
- 1)設置好待解封裝的資源。
- 在
mDemuxerConfig中實現(xiàn),我們這里是一個 MP4 文件。
- 在
- 2)啟動封裝器。
- 在
start中實現(xiàn)。 - 設置音視頻格式描述。
- 在
- 3)讀取解封裝后的音視頻編碼數(shù)據(jù)并送給封裝器進行重新封裝。
- 在
onClick中實現(xiàn)。
- 在
4、用工具播放 MP4 文件
完成 Demo 后,可以將 sdcard 文件夾下面的 test.mp4 文件拷貝到電腦上,使用 ffplay播放來驗證一下效果是否符合預期:
$ ffplay -I test.mp4
關于播放 MP4 文件的工具,可以參考《FFmpeg 工具》第 2 節(jié) ffplay 命令行工具和《可視化音視頻分析工具》第 3.5 節(jié) VLC 播放器。
我們還可以用《可視化音視頻分析工具》第 3.1 節(jié) MP4Box.js 等工具來查看它的格式。
- 完 -
推薦閱讀