H264这么强大,那想把YUV编码成可以播放的 H264怎么搞呢?
-
FFmpeg 这是老资格了,但是官网并没有提供安卓版本,只能自己去官网下载源码,用AndroidNDK去编译成Android版本的库,再去学习一下FFmpeg 的使用。
-
MediaCodec ,是安卓内部的提供对视频流硬件编码的一个类,很接近底层了,因为它的实现是由C++去实现的。我们只需要在Java层去调用接口,把YUV的数据编译成H264的流。
在这里我选择第二种,因为我第一种根本没有编译成功……好无语……
关于这个的使用,我也不是很清楚,我这里封装了一个类 MediaCodeUtil.kt
import android.media.MediaCodec
import android.media.MediaFormat
import android.media.MediaCodecInfo
import java.io.IOException
import android.media.MediaCodecList
import android.util.Log
import java.nio.ByteBuffer
/**
* Created by xiaolei on 2018/3/28.
*/
object MediaCodeUtil
{
private lateinit var mediaCodec: MediaCodec
private val VCODEC_MIME = "video/avc" // H264的IMEI
private var HEIGHT: Int = 0
private var WIDTH: Int = 0
/**
* @param width 宽度
* @param height 高度
* @param framerate 帧频,帧速率,帧数
*/
fun initMediaCodec(width: Int, height: Int, frame_rate: Int)
{
val bitrate = 2 * width * height * frame_rate / 20
try
{
if (!::mediaCodec.isInitialized)
{
WIDTH = width
HEIGHT = height
val mediaCodecInfo = selectCodec(VCODEC_MIME)
?: throw RuntimeException("mediaCodecInfo is Empty")
mediaCodec = MediaCodec.createByCodecName(mediaCodecInfo.name)
val mediaFormat = MediaFormat.createVideoFormat(VCODEC_MIME, width, height) // 设置需要编译的类型,以及视频的宽高
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate) // 关键的比特率
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, frame_rate) // 帧率
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,
MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible) // 输入的数据类型,是YUV
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1) // 我关键帧间隔
mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
mediaCodec.start()
}
} catch (e: IOException)
{
e.printStackTrace()
}
}
private fun selectCodec(mimeType: String): MediaCodecInfo?
{
val numCodecs = MediaCodecList.getCodecCount()
for (i in 0 until numCodecs)
{
val codecInfo = MediaCodecList.getCodecInfoAt(i)
//是否是编码器
if (!codecInfo.isEncoder)
{
continue
}
val types = codecInfo.supportedTypes
for (type in types)
{
if (mimeType.equals(type, ignoreCase = true))
{
return codecInfo
}
}
}
return null
}
/**
* 开始转换
* @param buf 输入的需要转换的数据
* @param callback 转换成功的回调
*/
fun conver(buf: ByteArray, callback: ((ByteBuffer) -> Unit))
{
// val LENGTH = HEIGHT * WIDTH
val inputBuffers = mediaCodec.inputBuffers
val outputBuffers = mediaCodec.outputBuffers
try
{
//查找可用的的input buffer用来填充有效数据
val bufferIndex = mediaCodec.dequeueInputBuffer(-1)
if (bufferIndex >= 0)
{
//数据放入到inputBuffer中
val inputBuffer = inputBuffers[bufferIndex]
inputBuffer.clear()
inputBuffer.put(buf, 0, buf.size)
//把数据传给编码器并进行编码
mediaCodec.queueInputBuffer(bufferIndex, 0,
inputBuffers[bufferIndex].position(),
System.nanoTime() / 1000, 0)
val bufferInfo = MediaCodec.BufferInfo()
//输出buffer出队,返回成功的buffer索引。
var outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0)
while (outputBufferIndex >= 0)
{
val outputBuffer = outputBuffers[outputBufferIndex]
// 回调,外部处理
callback(outputBuffer)
mediaCodec.releaseOutputBuffer(outputBufferIndex, false)
outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0)
}
} else
{
Log.e("XIAOLEI", "No buffer available !")
}
} catch (e: Exception)
{
e.printStackTrace()
}
}
}
看到没有,这里还是需要你的视频的宽高,所以前面为啥一直强调视频的宽高很重要。