content: package com.faceunity.encoder;
  
  import android.media.MediaCodec;
  import android.media.MediaCodecInfo;
  import android.media.MediaCodecList;
  import android.media.MediaFormat;
  import android.opengl.EGLContext;
  import android.opengl.GLES20;
  import android.util.Log;
  import android.view.Surface;
  
  import com.faceunity.gles.ProgramTexture2d;
  import com.faceunity.gles.core.GlUtil;
  
  import java.io.IOException;
  
  public class MediaVideoEncoder extends MediaEncoder {
      private static final String TAG = MediaVideoEncoder.class.getSimpleName();
      private static final boolean DEBUG = false;
  
      private static final String MIME_TYPE = "video/avc";
      // parameters for recording
      private static final int FRAME_RATE = 25;
      private static final int IFRAME_INTERVAL = 10;       // I-frames间隔时间
      private static final float BPP = 0.25f;
  
      private final int mWidth;
      private final int mHeight;
      private RenderHandler mRenderHandler;
      private Surface mSurface;
  
      private ProgramTexture2d program;
      private int[] mTextureId;
      private int[] mFBOId;
      private int[] mViewPort = new int[4];
      private int mFrameCount;
  
      public MediaVideoEncoder(final MediaMuxerWrapper muxer, final MediaEncoderListener listener, final int width, final int height) {
          super(muxer, listener);
          if (DEBUG) Log.i(TAG, "MediaVideoEncoder: ");
          mWidth = width;
          mHeight = height;
          mRenderHandler = RenderHandler.createHandler(TAG);
      }
  
      /**
       * select the first codec that match a specific MIME type
       *
       * @param mimeType
       * @return null if no codec matched
       */
      protected static final MediaCodecInfo selectVideoCodec(final String mimeType) {
          if (DEBUG)
              Log.v(TAG, "selectVideoCodec: " + mimeType);
  
          // get the list of available codecs
          final int numCodecs = MediaCodecList.getCodecCount();
          for (int i = 0; i < numCodecs; i++) {
              final MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
  
              if (!codecInfo.isEncoder()) {    // skipp decoder
                  continue;
              }
              // select first codec that match a specific MIME type and color format
              final String[] types = codecInfo.getSupportedTypes();
              for (int j = 0; j < types.length; j++) {
                  if (types[j].equalsIgnoreCase(mimeType)) {
                      if (DEBUG)
                          Log.i(TAG, "codec:" + codecInfo.getName() + ",MIME=" + types[j]);
                      final int format = selectColorFormat(codecInfo, mimeType);
                      if (format > 0) {
                          return codecInfo;
                      }
                  }
              }
          }
          return null;
      }
  
      @Override
      protected void prepare() throws IOException {
          if (DEBUG) Log.i(TAG, "prepare: ");
          mTrackIndex = -1;
          mMuxerStarted = mIsEOS = false;
  
          final MediaCodecInfo videoCodecInfo = selectVideoCodec(MIME_TYPE);
          if (videoCodecInfo == null) {
              Log.e(TAG, "Unable to find an appropriate codec for " + MIME_TYPE);
              return;
          }
          if (DEBUG) Log.i(TAG, "selected codec: " + videoCodecInfo.getName());
  
          final MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, mWidth, mHeight);
          format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);    // API >= 18
          format.setInteger(MediaFormat.KEY_BIT_RATE, calcBitRate());
          format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);
          format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
          if (DEBUG) Log.i(TAG, "format: " + format);
  
          mMediaCodec = MediaCodec.createEncoderByType(MIME_TYPE);
          mMediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
          // get Surface for encoder input
          // this method only can call between #configure and #start
          mSurface = mMediaCodec.createInputSurface();    // API >= 18
          mMediaCodec.start();
          if (DEBUG) Log.i(TAG, "prepare finishing");
          if (mListener != null) {
              try {
                  mListener.onPrepared(this);
              } catch (final Exception e) {
                  Log.e(TAG, "prepare:", e);
              }
          }
      }
  
      public boolean frameAvailableSoon(int texId, final float[] texMatrix, float[] mvpMatrix) {
          GLES20.glGetIntegerv(GLES20.GL_VIEWPORT, mViewPort, 0);
          GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFBOId[0]);
          GLES20.glViewport(0, 0, mWidth, mHeight);
          program.drawFrame(texId, texMatrix, mvpMatrix);
          GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
          GLES20.glViewport(mViewPort[0], mViewPort[1], mViewPort[2], mViewPort[3]);
          // 先绘制三次，不进行编码，解决黑屏问题
          if (mFrameCount++ < 3) {
              return true;
          }
          boolean result;
          if (result = super.frameAvailableSoon()) {
              mRenderHandler.draw(mTextureId[0], GlUtil.IDENTITY_MATRIX, mvpMatrix);
          }
          return result;
      }
  
      public void setEglContext(final EGLContext shared_context) {
          mTextureId = new int[1];
          mFBOId = new int[1];
          GlUtil.createFBO(mTextureId, mFBOId, mWidth, mHeight);
          program = new ProgramTexture2d();
          mRenderHandler.setEglContext(shared_context, mSurface, mTextureId[0]);
      }
  
      private int calcBitRate() {
          final int bitrate = (int) (BPP * FRAME_RATE * mWidth * mHeight);
          Log.i(TAG, String.format("bitrate=%5.2f[Mbps]", bitrate / 1024f / 1024f));
          return bitrate;
      }
  
      @Override
      protected void release() {
          if (DEBUG) Log.i(TAG, "release:");
          if (mSurface != null) {
              mSurface.release();
              mSurface = null;
          }
          if (mRenderHandler != null) {
              mRenderHandler.release();
              mRenderHandler = null;
          }
          GlUtil.deleteFBO(mFBOId);
          GlUtil.deleteTextureId(mTextureId);
          if (program != null) {
              program.release();
              program = null;
          }
          mFrameCount = 0;
          super.release();
      }
  
      /**
       * select color format available on specific codec and we can use.
       *
       * @return 0 if no colorFormat is matched
       */
      protected static final int selectColorFormat(final MediaCodecInfo codecInfo, final String mimeType) {
          if (DEBUG) Log.i(TAG, "selectColorFormat: " + mimeType);
          int result = 0;
          final MediaCodecInfo.CodecCapabilities caps;
          try {
              Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
              caps = codecInfo.getCapabilitiesForType(mimeType);
          } finally {
              Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
          }
          int colorFormat;
          for (int i = 0; i < caps.colorFormats.length; i++) {
              colorFormat = caps.colorFormats[i];
              if (isRecognizedViewoFormat(colorFormat)) {
                  if (result == 0)
                      result = colorFormat;
                  break;
              }
          }
          if (result == 0)
              Log.e(TAG, "couldn't find a good color format for " + codecInfo.getName() + " / " + mimeType);
          return result;
      }
  
      /**
       * color formats that we can use in this class
       */
      protected static int[] recognizedFormats;
  
      static {
          recognizedFormats = new int[]{
  //        	MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar,
  //        	MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar,
  //        	MediaCodecInfo.CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar,
                  MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface,
          };
      }
  
      private static final boolean isRecognizedViewoFormat(final int colorFormat) {
          if (DEBUG) Log.i(TAG, "isRecognizedViewoFormat:colorFormat=" + colorFormat);
          final int n = recognizedFormats != null ? recognizedFormats.length : 0;
          for (int i = 0; i < n; i++) {
              if (recognizedFormats[i] == colorFormat) {
                  return true;
              }
          }
          return false;
      }
  
      @Override
      protected void signalEndOfInputStream() {
          if (DEBUG) Log.d(TAG, "sending EOS to encoder");
          mMediaCodec.signalEndOfInputStream();    // API >= 18
          mIsEOS = true;
      }
  
  }
  