package com.cagneymoreau.video; import android.media.MediaCodec; import android.media.MediaFormat; import android.os.Handler; import android.os.HandlerThread; import android.util.Log; import com.cagneymoreau.commchannel.R; import com.cagneymoreau.network.RobotPoint; import com.cagneymoreau.videoencoding.TransferEncodedH264; import com.github.piasy.videocre.MediaCodecCallback; import org.webrtc.EglBase; import org.webrtc.MediaCodecVideoEncoder; import org.webrtc.VideoCapturer; import org.webrtc.VideoFrame; import org.webrtc.VideoRenderer; import java.util.List; import java.util.concurrent.TimeUnit; public class AvccEncoder implements VideoRenderer.Callbacks, MediaCodecCallback { private final String TAG = "AvccEncoder"; HandlerThread mediaCoderThread; Handler mediaCodecHandler; MediaCodecVideoEncoder videoEncoder; List encodedBuffer; //my buffer as a list which holds the h.264 nals Object lock; // my lock byte[] b; private long start, duration; int bufferque = 0; long[] holder = new long[40]; long calced = 0; int ticks=0; long sum; int average; public AvccEncoder(RobotPoint robotPoint) { Log.d(TAG, "AvccEncoder: "); //get streaming objects Packetize packetize = new Packetize(robotPoint, robotPoint.getH264Socket()); encodedBuffer= packetize.gettoSend(); lock = packetize.getLock(); packetize.start(); mediaCoderThread = new HandlerThread("avccencodethread"); mediaCoderThread.start(); mediaCodecHandler = new Handler(mediaCoderThread.getLooper()); videoEncoder = new MediaCodecVideoEncoder(); } public void start(final EglBase eglBase) { mediaCodecHandler.post(new Runnable() { @Override public void run() { videoEncoder.initEncode(MediaCodecVideoEncoder.VideoCodecType.VIDEO_CODEC_H264, MediaCodecVideoEncoder.H264Profile.CONSTRAINED_BASELINE.getValue(), 320, 240, 8000, 30, eglBase.getEglBaseContext(),AvccEncoder.this); } }); } /** * Called to send a frame into the encoder */ @Override public void renderFrame(VideoRenderer.I420Frame i420Frame) { start = System.nanoTime(); bufferque++; mediaCodecHandler.post(new Runnable() { @Override public void run() { videoEncoder.encodeTexture(false, i420Frame.textureId, i420Frame.samplingMatrix, TimeUnit.NANOSECONDS.toMicros(i420Frame.timestamp)); } }); } /** * Called to retrieve an encoded frame */ @Override public void onEncodedFrame(MediaCodecVideoEncoder.OutputBufferInfo frame, MediaCodec.BufferInfo bufferInfo) { b = new byte[frame.buffer().remaining()]; frame.buffer().get(b); synchronized (lock) { encodedBuffer.add(b); lock.notifyAll(); if(encodedBuffer.size() > 1) { Log.e(TAG, "drainEncoder: too big: " + encodedBuffer.size(),null ); } } duration = System.nanoTime() - start; bufferque--; calcAverage(); if (bufferque > 0) { Log.d(TAG, "onEncodedFrame: bufferque size: " + bufferque); } } @Override public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) { } public void destroy() { mediaCodecHandler.post(new Runnable() { @Override public void run() { videoEncoder.release(); mediaCoderThread.quit(); } }); } private void calcAverage() { ticks++; calced = duration; if(ticks > 60) { //slower method to copy and display for (int i = 1; i < holder.length; i++) { sum += holder[i]; holder[i-1] = holder[i]; } holder[holder.length-1] = calced; sum += calced; average = (int) (sum/ holder.length) / 1000000; //average in millis ticks = 0; sum = 0; Log.d(TAG, "millis per loop " + average); }else { //do a fast copy System.arraycopy(holder, 1,holder,0,holder.length-1); holder[holder.length-1] = calced; } } }