vx 搜索『gjzkeyframe』 關(guān)注『關(guān)鍵幀Keyframe』來及時獲得最新的音視頻技術(shù)文章揽乱。
iOS/Android 客戶端開發(fā)同學(xué)如果想要開始學(xué)習(xí)音視頻開發(fā)隙疚,最絲滑的方式是對音視頻基礎(chǔ)概念知識有一定了解后尾组,再借助 iOS/Android 平臺的音視頻能力上手去實踐音視頻的采集 → 編碼 → 封裝 → 解封裝 → 解碼 → 渲染
過程忙芒,并借助音視頻工具來分析和理解對應(yīng)的音視頻數(shù)據(jù)。
iOS/Android 客戶端開發(fā)同學(xué)如果想要開始學(xué)習(xí)音視頻開發(fā)演怎,最絲滑的方式是對音視頻基礎(chǔ)概念知識有一定了解后匕争,再借助 iOS/Android 平臺的音視頻能力上手去實踐音視頻的采集 → 編碼 → 封裝 → 解封裝 → 解碼 → 渲染
過程,并借助音視頻工具來分析和理解對應(yīng)的音視頻數(shù)據(jù)爷耀。
在音視頻工程示例這個欄目甘桑,我們將通過拆解采集 → 編碼 → 封裝 → 解封裝 → 解碼 → 渲染
流程并實現(xiàn) Demo 來向大家介紹如何在 iOS/Android 平臺上手音視頻開發(fā)。
這里是 Android 第十篇:Android 視頻解封裝 Demo歹叮。這個 Demo 里包含以下內(nèi)容:
- 1)實現(xiàn)一個視頻解封裝模塊跑杭;
- 2)實現(xiàn)對 MP4 文件中視頻部分的解封裝邏輯并將解封裝后的編碼數(shù)據(jù)存儲為 H.264/H.265 文件;
- 3)詳盡的代碼注釋咆耿,幫你理解代碼邏輯和原理德谅。
在本文中,我們將詳解一下 Demo 的具體實現(xiàn)和源碼萨螺。讀完本文內(nèi)容相信就能幫你掌握相關(guān)知識窄做。
不過,如果你的需求是:1)直接獲得全部工程源碼慰技;2)想進(jìn)一步咨詢音視頻技術(shù)問題椭盏;3)咨詢音視頻職業(yè)發(fā)展問題∥巧蹋可以根據(jù)自己的需要考慮是否加入『關(guān)鍵幀的音視頻開發(fā)圈』掏颊。
1、視頻解封裝模塊
視頻解封裝模塊即 KFMP4Demuxer
,復(fù)用了《Android 音頻解封裝 Demo》中介紹的 demuxer乌叶,這里就不再重復(fù)介紹了盆偿,其接口如下:
KFMP4Demuxer.java
public class KFMP4Demuxer {
public KFMP4Demuxer(KFDemuxerConfig config, KFDemuxerListener listener); ///< 構(gòu)造方法:配置 & 回調(diào)。
public void release(); ///< 釋放解封裝器實例准浴。
public boolean hasVideo(); ///< 是否包含視頻事扭。
public boolean hasAudio(); ///< 是否包含音頻。
public int duration(); ///< 文件時長兄裂。
public int rotation(); ///< 視頻旋轉(zhuǎn)角度句旱。
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、解封裝 MP4 文件中的視頻部分存儲為 H.264/H.265 文件
我們還是在一個 MainActivity
中來實現(xiàn)對一個 MP4 文件解封裝矛纹、獲取其中的視頻編碼數(shù)據(jù)并存儲為 H.264/H.265 文件臂聋。
MainActivity.java
public class MainActivity extends AppCompatActivity {
private KFMP4Demuxer mDemuxer; ///< 解封裝器。
private KFDemuxerConfig mDemuxerConfig; ///< 解封裝配置或南。
private FileOutputStream mStream = null;
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
///< 申請采集孩等、存儲權(quán)限。
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.KFMediaVideo;
if (mStream == null) {
try {
mStream = new FileOutputStream(Environment.getExternalStorageDirectory().getPath() + "/test.h264");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
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);
///< 根據(jù) HEVC 分別獲取 vps蹬癌、sps权她、pps 等信息。
if (mDemuxer.isHEVC()) {
try {
ByteBuffer extradata = mDemuxer.videoMediaFormat().getByteBuffer("csd-0");
byte[] extradataBytes = new byte[extradata.capacity()];
extradata.get(extradataBytes);
mStream.write(extradataBytes);
} catch (IOException e) {
e.printStackTrace();
}
} else {
try {
ByteBuffer sps = mDemuxer.videoMediaFormat().getByteBuffer("csd-0");
byte[] spsBytes = new byte[sps.capacity()];
sps.get(spsBytes);
mStream.write(spsBytes);
} catch (IOException e) {
e.printStackTrace();
}
try {
ByteBuffer pps = mDemuxer.videoMediaFormat().getByteBuffer("csd-1");
byte[] ppsBytes = new byte[pps.capacity()];
pps.get(ppsBytes);
mStream.write(ppsBytes);
} catch (IOException e) {
e.printStackTrace();
}
}
///< 循環(huán)讀取視頻數(shù)據(jù)逝薪。
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
ByteBuffer nextBuffer = mDemuxer.readVideoSampleData(bufferInfo);
while (nextBuffer != null) {
try {
byte[] dst = new byte[bufferInfo.size];
nextBuffer.get(dst);
mStream.write(dst);
} catch (IOException e) {
e.printStackTrace();
}
nextBuffer = mDemuxer.readVideoSampleData(bufferInfo);
}
Log.i("KFDemuxer","complete");
}
}
});
addContentView(startButton, startParams);
}
private KFDemuxerListener mDemuxerListener = new KFDemuxerListener() {
@Override
///< 解封裝出錯回調(diào)隅要。
public void demuxerOnError(int error, String errorMsg) {
Log.i("KFDemuxer","error" + error + "msg" + errorMsg);
}
};
}
上面是 MainActivity
的實現(xiàn),其中主要包含這幾個部分:
- 1)設(shè)置好待解封裝的資源翼闽。
- 在
mDemuxerConfig
中實現(xiàn)拾徙,我們這里是一個 MP4 文件。
- 在
- 2)創(chuàng)建解封裝器感局。
-
new KFMP4Demuxer(mDemuxerConfig,mDemuxerListener)
尼啡。
-
- 3)讀取解封裝后的視頻編碼數(shù)據(jù)并存儲為 H.264/H.265 文件暂衡。
- 循環(huán)讀取
readVideoSampleData
H.264/H.265 裸數(shù)據(jù)。 - 需要注意的是崖瞭,我們從解封裝器讀取的音頻 H.264/H.265 編碼數(shù)據(jù)在存儲文件時需要添加 VPS狂巢、PPS、SPS 信息书聚。
- 循環(huán)讀取
3唧领、用工具播放 H.264/H.265 文件
完成視頻解封裝后,可以將 sdcard
文件夾下面的 test.h264
或 test.h265
文件拷貝到電腦上雌续,使用 ffplay
播放來驗證一下視頻解封裝的效果是否符合預(yù)期:
$ ffplay -I test.h264
$ ffplay -I test.h265
關(guān)于播放 H.264/H.265 文件的工具斩个,可以參考《FFmpeg 工具》第 2 節(jié) ffplay 命令行工具和《可視化音視頻分析工具》第 2.1 節(jié) StreamEye。
- 完 -
推薦閱讀