在网络视频直播系统中,流媒体传输流程涉及了很多环节,像采集、编码、解码、传输、处理等,其中解码关系着直播内容呈现的效果,所以需要引起足够的重视,在网络视频直播系统中解码的实现流程是什么样的呢?
解码流程
1、初识化,启动解码器
根据MediaFormat中的编码类型(如video/avc,即H264;audio/mp4a-latm,即AAC)创建MediaCodec
//通过Extractor获取到音视频数据的编码信息MediaFormat
val type = mExtractor!!.getFormat()!!.getString(MediaFormat.KEY_MIME)
//调用createDecoderByType创建解码器。
mCodec = MediaCodec.createDecoderByType(type)
2、将网络视频直播系统数据压入解码器输入缓冲
如果SampleSize返回-1,说明没有更多的数据了。queueInputBuffer的最后一个参数要传入结束标记MediaCodec.BUFFER_FLAG_END_OF_STREAM。
//查询是否有可用的输入缓冲,返回缓冲索引
var inputBufferIndex = mCodec!!.dequeueInputBuffer(2000)
//获取可用的缓冲区,并使用Extractor提取待解码数据,填充到缓冲区中。
val inputBuffer = mInputBuffers!![inputBufferIndex]
val sampleSize = mExtractor!!.readBuffer(inputBuffer)
//调用queueInputBuffer将数据压入解码器。
mCodec!!.queueInputBuffer(inputBufferIndex, 0,sampleSize, mExtractor!!.getCurrentTimestamp(), 0)
/**
* 读取视频数据
*/
fun readBuffer(byteBuffer: ByteBuffer): Int {
//【3,提取数据】
byteBuffer.clear()
selectSourceTrack()
var readSampleCount = mExtractor!!.readSampleData(byteBuffer, 0)
if (readSampleCount < 0) {
return -1
}
mCurSampleTime = mExtractor!!.sampleTime
mExtractor!!.advance()
return readSampleCount
}
/**
* 选择通道
*/
private fun selectSourceTrack() {
if (mVideoTrack >= 0) {
mExtractor!!.selectTrack(mVideoTrack)
} else if (mAudioTrack >= 0) {
mExtractor!!.selectTrack(mAudioTrack)
}
}
3、将解码好的数据从网络视频直播系统缓冲区拉取出来
// 查询是否有解码完成的数据,index >=0 时,表示数据有效,并且index为缓冲区索引
var index = mCodec!!.dequeueOutputBuffer(mBufferInfo, 1000)
when (index) {
//输出格式改变了
MediaCodec.INFO_OUTPUT_FORMAT_CHANGED -> {}
//没有可用数据,等会再来
MediaCodec.INFO_TRY_AGAIN_LATER -> {}
//输入缓冲改变了
MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED -> {
mOutputBuffers = mCodec!!.outputBuffers
}
else -> {
return index
}
}
4、渲染
视频渲染
网络视频直播系统中视频的渲染并不需要客户端手动去渲染,只需在config时提供绘制表面surface,如果为编解码器配置了输出表面,则将render设置为true会首先将缓冲区发送到该输出表面。
public void configure (MediaFormat format, Surface surface, MediaCrypto crypto, int flags)
releaseOutputBuffer (int index, boolean render)
音频渲染
config时音频不需要surface,直接传null。需要获取采样率,通道数,采样位数等。需要初始化一个网络视频直播系统音频渲染器:AudioTrack
AudioTrack只能播放PCM数据流。
//获取最小缓冲区
val minBufferSize = AudioTrack.getMinBufferSize(mSampleRate, channel, mPCMEncodeBit)
mAudioTrack = AudioTrack(
AudioManager.STREAM_MUSIC,//播放类型:音乐
mSampleRate, //采样率
channel, //通道
mPCMEncodeBit, //采样位数
minBufferSize, //缓冲区大小
AudioTrack.MODE_STREAM) //播放模式:数据流动态写入,另一种是一次性写入
mAudioTrack!!.play()
AudioTrack中有MODE_STATIC和MODE_STREAM两种分类。STREAM的意思是由用户在应用程序通过write方式把数据一次一次得写到audiotrack中。这个和我们在socket中发送数据一样,AudioTrack应用层从某个地方获取数据,例如通过编解码得到PCM数据,然后write到audiotrack。这种方式的坏处就是总是在JAVA层和Native层交互,效率损失较大。
而STATIC的意思是一开始创建的时候,就把音频数据放到一个固定的buffer,然后直接传给audiotrack,后续就不用一次次得write了。AudioTrack会自己播放这个buffer中的数据。这种方法对于AudioTrack铃声等内存占用较小,延时要求较高的声音来说很适用
5、释放输出缓冲
mCodec!!.releaseOutputBuffer(index, true)
6、判断解码是否完成
if (mBufferInfo.flags == MediaCodec.BUFFER_FLAG_END_OF_STREAM) {
}
7、释放解码器
释放掉所有的资源。至此,网络视频直播系统一次解码结束。
mExtractor?.stop()
mCodec?.stop()
mCodec?.release()
/**
* 停止读取数据
*/
fun stop() {
//【4,释放提取器】
mExtractor?.release()
mExtractor = null
}
以上便是“网络视频直播系统的流媒体传输中解码的实现流程”的全部内容,希望对大家有帮助。