- 1 public class AvcKeyFrameEncoder {
- 2 private final static String TAG = "MeidaCodec";
- 3 private int TIMEOUT_USEC = 12000;
- 4
- 5 private MediaCodec mediaCodec;
- 6 int m_width;
- 7 int m_height;
- 8 int m_framerate;
- 9
- 10 public byte[] configbyte;
- 11
- 12 //待解码视频缓冲队列,静态成员!
- 13 public byte[] yuv_data = null;
- 14 public long stamptime = 0;
- 15
- 16 public AvcKeyFrameEncoder(int width, int height, int framerate) {
- 17 m_width = width;
- 18 m_height = height;
- 19 m_framerate = framerate;
- 20
- 21 //正常的编码出来是横屏的。因为手机本身采集的数据默认就是横屏的
- 22 // MediaFormat mediaFormat = MediaFormat.createVideoFormat(mime, width, height);
- 23 //如果你需要旋转90度或者270度,那么需要把宽和高对调。否则会花屏。因为比如你320 X 240,图像旋转90°之后宽高变成了240 X 320。
- 24 MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc", width, height);
- 25 mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar);
- 26 mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 125000);
- 27 mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, framerate); // 30
- 28 mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
- 29 try {
- 30 mediaCodec = MediaCodec.createEncoderByType("video/avc");
- 31 } catch (IOException e) {
- 32 e.printStackTrace();
- 33 }
- 34
- 35 //配置编码器参数
- 36 mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
- 37
- 38 //启动编码器
- 39 mediaCodec.start();
- 40 }
- 41
- 42 public void StopEncoder() {
- 43 try {
- 44 mediaCodec.stop();
- 45 mediaCodec.release();
- 46 } catch (Exception e) {
- 47 e.printStackTrace();
- 48 }
- 49 }
- 50
- 51 public boolean isRuning = false;
- 52
- 53 public void StartEncoderThread(final ISaveVideo saveVideo, final ICall callback) {
- 54 isRuning = true;
- 55 new Thread(new Runnable() {
- 56 @Override
- 57 public void run() {
- 58 byte[] input = null;
- 59 long pts = 0;
- 60 while (isRuning) {
- 61 // 访问MainActivity用来缓冲待解码数据的队列
- 62 if(yuv_data == null){
- 63 continue;
- 64 }
- 65
- 66 if (yuv_data != null) {
- 67 //从缓冲队列中取出一帧
- 68 input = yuv_data;
- 69 pts = stamptime;
- 70 yuv_data = null;
- 71 byte[] yuv420sp = new byte[m_width * m_height * 3 / 2];
- 72
- 73 NV21ToNV12(input, yuv420sp, m_width, m_height);
- 74 input = yuv420sp;
- 75 }
- 76
- 77 if (input != null) {
- 78 try {
- 79 //编码器输入缓冲区
- 80 ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers();
- 81
- 82 //编码器输出缓冲区
- 83 ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers();
- 84 int inputBufferIndex = mediaCodec.dequeueInputBuffer(-1);
- 85 if (inputBufferIndex >= 0) {
- 86 ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
- 87 inputBuffer.clear();
- 88 //把转换后的YUV420格式的视频帧放到编码器输入缓冲区中
- 89 inputBuffer.put(input);
- 90 mediaCodec.queueInputBuffer(inputBufferIndex, 0, input.length, pts, 0);
- 91 }
- 92
- 93 MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
- 94 int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC);
- 95 while (outputBufferIndex >= 0) {
- 96 //Log.i("AvcEncoder", "Get H264 Buffer Success! flag = "+bufferInfo.flags+",pts = "+bufferInfo.presentationTimeUs+"");
- 97 ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
- 98 byte[] outData = new byte[bufferInfo.size];
- 99 outputBuffer.get(outData);
- 100 if (bufferInfo.flags == BUFFER_FLAG_CODEC_CONFIG) {
- 101 configbyte = new byte[bufferInfo.size];
- 102 configbyte = outData;
- 103 } else if (bufferInfo.flags == BUFFER_FLAG_KEY_FRAME) {
- 104 byte[] keyframe = new byte[bufferInfo.size + configbyte.length];
- 105 System.arraycopy(configbyte, 0, keyframe, 0, configbyte.length);
- 106 //把编码后的视频帧从编码器输出缓冲区中拷贝出来
- 107 System.arraycopy(outData, 0, keyframe, configbyte.length, outData.length);
- 108
- 109 Logs.i("上传I帧 " + keyframe.length);
- 110 byte[] send_data = new byte[13 + keyframe.length];
- 111 System.arraycopy(new byte[]{0x01}, 0, send_data, 0, 1);
- 112 System.arraycopy(IntBytes.longToBytes(pts), 0, send_data, 1, 8);
- 113 System.arraycopy(IntBytes.intToByteArray(keyframe.length), 0, send_data, 9, 4);
- 114 System.arraycopy(keyframe, 0, send_data, 13, keyframe.length);
- 115 if(saveVideo != null){
- 116 saveVideo.SaveVideoData(send_data);
- 117 }
- 118
- 119 if(callback != null){
- 120 callback.callback(keyframe, pts);
- 121 }
- 122 } else {
- 123 byte[] send_data = new byte[13 + outData.length];
- 124 System.arraycopy(new byte[]{0x02}, 0, send_data, 0, 1);
- 125 System.arraycopy(IntBytes.longToBytes(pts), 0, send_data, 1, 8);
- 126 System.arraycopy(IntBytes.intToByteArray(outData.length), 0, send_data, 9, 4);
- 127 System.arraycopy(outData, 0, send_data, 13, outData.length);
- 128 if(saveVideo != null){
- 129 saveVideo.SaveVideoData(send_data);
- 130 }
- 131
- 132 if(callback != null){
- 133 callback.callback(outData, pts);
- 134 }
- 135 }
- 136
- 137 mediaCodec.releaseOutputBuffer(outputBufferIndex, false);
- 138 outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC);
- 139 }
- 140
- 141 } catch (Throwable t) {
- 142 t.printStackTrace();
- 143 break;
- 144 }
- 145 }
- 146 }
- 147 }
- 148 }).start();
- 149 }
- 150
- 151 private void NV21ToNV12(byte[] nv21, byte[] nv12, int width, int height) {
- 152 if (nv21 == null || nv12 == null) return;
- 153 int framesize = width * height;
- 154 int i = 0, j = 0;
- 155 System.arraycopy(nv21, 0, nv12, 0, framesize);
- 156 for (i = 0; i < framesize; i++) {
- 157 nv12[i] = nv21[i];
- 158 }
- 159
- 160 for (j = 0; j < framesize / 2; j += 2) {
- 161 nv12[framesize + j - 1] = nv21[j + framesize];
- 162 }
- 163
- 164 for (j = 0; j < framesize / 2; j += 2) {
- 165 nv12[framesize + j] = nv21[j + framesize - 1];
- 166 }
- 167 }
- 168 }