Android视频直播流(五)MediaCodec

H264这么强大,那想把YUV编码成可以播放的 H264怎么搞呢?

  1. FFmpeg 这是老资格了,但是官网并没有提供安卓版本,只能自己去官网下载源码,用AndroidNDK去编译成Android版本的库,再去学习一下FFmpeg 的使用。

  2. 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()
        }
    }
}

看到没有,这里还是需要你的视频的宽高,所以前面为啥一直强调视频的宽高很重要。

猜你喜欢

转载自my.oschina.net/xiaolei123/blog/1786831
今日推荐