Android yuv编码为h264

本文介绍了如何使用Android的MediaCodec API进行视频编码,包括创建编码器、设置编码参数、输入输出缓冲区的处理等关键步骤,详细讲解了音频编码与视频编码API的异同,并提供了完整的编码示例代码。
摘要由CSDN通过智能技术生成

视频基础知识

音频编码和视频编码用的api是一样的,不一样的地方是对编码器的设置

public void init(Context context, int width,
                     int height,
                     String srcPath, String dstPath,
                     IHanlderCallback callback) {
        mWidth = width;
        mHeight = height;
        mSrcFilePath = srcPath;
        mDstFilePath = dstPath;
        mExecutorService = Executors.newCachedThreadPool();
        mCallback = callback;
        mBufferSize =mWidth * mHeight * 3 / 2;
        Log.e("yuanBuffer", "buffesize=" + mBufferSize + " nbsample=" + mBufferSize / 4);
        try {
            mEncorder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);

        } catch (Exception e) {
            e.printStackTrace();
        }
        MediaFormat format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC,
                mWidth,
                mHeight);
        format.setInteger(MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.AVCProfileMain);
        format.setInteger(MediaFormat.KEY_LEVEL, MediaCodecInfo.CodecProfileLevel.AVCLevel31);
        format.setInteger(MediaFormat.KEY_BIT_RATE, 1000 * 1000);
        format.setInteger(MediaFormat.KEY_FRAME_RATE, 25);//帧率,一般在15至30之内,太小容易造成视频卡顿。
        format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 10);  //250帧一个关键帧
        format.setInteger(MediaFormat.KEY_MAX_B_FRAMES, 3);
        format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar);//色彩格式,具体查看相关API,不同设备支持的色彩格式不尽相同
        mEncorder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
    }

这里的配置会根据

https://blog.csdn.net/qq_15255121/article/details/115552857

这篇文章进行配置

下面是完整的代码

package com.yuanxuzhen.androidmedia.encode;

import android.content.Context;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.os.Build;
import android.util.Log;

import androidx.annotation.RequiresApi;

import com.yuanxuzhen.androidmedia.IHanlderCallback;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class YuvEncoder {
    private int mWidth;
    private int mHeight;
    private String mDstFilePath = null;
    private String mSrcFilePath = null;
    MediaCodec mEncorder;
    ExecutorService mExecutorService;
    private int mBufferSize;
    private IHanlderCallback mCallback;
    private boolean isEncodeing = false;


    public void init(Context context, int width,
                     int height,
                     String srcPath, String dstPath,
                     IHanlderCallback callback) {
        mWidth = width;
        mHeight = height;
        mSrcFilePath = srcPath;
        mDstFilePath = dstPath;
        mExecutorService = Executors.newCachedThreadPool();
        mCallback = callback;
        mBufferSize =mWidth * mHeight * 3 / 2;
        Log.e("yuanBuffer", "buffesize=" + mBufferSize + " nbsample=" + mBufferSize / 4);
        try {
            mEncorder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);

        } catch (Exception e) {
            e.printStackTrace();
        }
        MediaFormat format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC,
                mWidth,
                mHeight);
        format.setInteger(MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.AVCProfileMain);
        format.setInteger(MediaFormat.KEY_LEVEL, MediaCodecInfo.CodecProfileLevel.AVCLevel31);
        format.setInteger(MediaFormat.KEY_BIT_RATE, 1000 * 1000);
        format.setInteger(MediaFormat.KEY_FRAME_RATE, 25);//帧率,一般在15至30之内,太小容易造成视频卡顿。
        format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 10);  //250帧一个关键帧
        format.setInteger(MediaFormat.KEY_MAX_B_FRAMES, 3);
        format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar);//色彩格式,具体查看相关API,不同设备支持的色彩格式不尽相同
        mEncorder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
    }
    public void startEncording() {
        mExecutorService.execute(new Runnable() {
            @Override
            public void run() {
                startEncordingV1();
            }
        });
    }

    /**
     * 开始编码
     */
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public void startEncordingV1() {
        if (mEncorder == null) {
            isEncodeing = false;
            if (mCallback != null) {
                mCallback.onFail();
            }
            return;
        }
        isEncodeing = true;
        mEncorder.start();
        try {
            FileInputStream inputStream = new FileInputStream(mSrcFilePath);
            FileOutputStream mFileStream = new FileOutputStream(mDstFilePath);
            MediaCodec.BufferInfo mBufferInfo = new MediaCodec.BufferInfo();
            while (true) {
                long a = System.currentTimeMillis();
                // 从队列中取出录音的一帧音频数据
                byte[] byteArray = new byte[mBufferSize];
                int readSize = inputStream.read(byteArray);
                if (readSize <= 0) {
                    break;
                }
                ByteBuffer buf = ByteBuffer.wrap(byteArray);
                // 取出InputBuffer,填充音频数据,然后输送到编码器进行编码
                int inputBufferIndex = mEncorder.dequeueInputBuffer(0);
                if (inputBufferIndex >= 0) {
                    ByteBuffer inputBuffer = mEncorder.getInputBuffer(inputBufferIndex);
                    inputBuffer.clear();
                    inputBuffer.put(buf);
                    mEncorder.queueInputBuffer(inputBufferIndex, 0, readSize, System.nanoTime(), 0);
                }

                // 取出编码好的一帧音频数据,然后给这一帧添加ADTS头
                int outputBufferIndex = mEncorder.dequeueOutputBuffer(mBufferInfo, 0);
                while (outputBufferIndex >= 0) {
                    ByteBuffer outputBuffer = mEncorder.getOutputBuffer(outputBufferIndex);
                    int outBufferSize = outputBuffer.limit();
                    byte[] aacBytes = new byte[outBufferSize];
                    outputBuffer.get(aacBytes, 0, outputBuffer.limit());
                    mFileStream.write(aacBytes);
                    mEncorder.releaseOutputBuffer(outputBufferIndex, false);
                    outputBufferIndex = mEncorder.dequeueOutputBuffer(mBufferInfo, 0);
                }

                long b = System.currentTimeMillis() - a;
                Log.i("AudioEncode", "编码耗时-毫秒==" + b);
            }
            if (mFileStream != null) {
                try {
                    mFileStream.flush();
                    mFileStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if (inputStream != null) {
                try {
                    mFileStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (mEncorder != null) {
                mEncorder.stop();
            }
            if (mCallback != null) {
                mCallback.onSuccess();
            }
        } catch (FileNotFoundException e) {
            if (mCallback != null) {
                mCallback.onFail();
            }
            e.printStackTrace();
        } catch (IOException e) {
            if (mCallback != null) {
                mCallback.onFail();
            }
            e.printStackTrace();
        } finally {
            isEncodeing = false;
        }
    }



    public void release() {
        if (mEncorder == null) {
            return;
        }
        mEncorder.release();
        mEncorder = null;
        isEncodeing = false;
    }

    public boolean isEncodeing() {
        return isEncodeing;
    }
}

gitee地址:

https://gitee.com/creat151/android-media.git

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值