背景:

  由于,项目需要,需要进行视频通信,把a的画面,转给b。

运维部署:

  APP1:编码摄像头采集的数据,并且发送数据到服务端

  APP2:从服务端,拉取数据,并且进行解码显示

  服务端:接收APP1提交的数据,发送APP1提交数据到APP2

应用说明:

  APP1:camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_FRONT);

        Camera.Parameters parameters = camera.getParameters();
        parameters.setPreviewFormat(ImageFormat.NV21);
        parameters.setPreviewSize(width, height);

        // 设置屏幕亮度
        parameters.setExposureCompensation(parameters.getMaxExposureCompensation() / 2);
        camera.setParameters(parameters);
        camera.setDisplayOrientation(90);
        camera.setPreviewCallback(new Camera.PreviewCallback() {
            @Override
            public void onPreviewFrame(byte[] data, Camera camera) {
// 采集视频数据,同时记录采集视频的时间点,解码需要(保证视频连续,流畅,且不花屏需要) stamptime
= System.nanoTime(); yuv_data = data; } });
  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 }
视频编码类Encoder

相关文章:

  • 2021-12-11
  • 2021-05-11
  • 2021-05-17
  • 2021-11-26
  • 2021-10-28
  • 2021-07-28
  • 2021-06-01
  • 2021-05-17
猜你喜欢
  • 2021-11-20
  • 2021-05-17
  • 2021-10-12
  • 2022-01-15
  • 2021-11-21
  • 2021-11-03
  • 2021-12-04
相关资源
相似解决方案