package com.a30corner.screenrecoder; import java.io.IOException; import java.nio.ByteBuffer; import android.content.Context; import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplay; import android.media.MediaCodec; import android.media.MediaCodec.BufferInfo; import android.media.MediaCodecInfo; import android.media.MediaFormat; import android.media.MediaMuxer; import android.media.MediaMuxer.OutputFormat; import android.util.Log; import android.view.Surface; public class ScreenRecoder { private static final String DISPLAY_NAME = "ScreenRecoder"; private final DisplayManager mDisplayManager; private int videoTrackIndex; private VirtualDisplayThread mVirtualDisplayThread; private volatile boolean started; public ScreenRecoder(Context context) { mDisplayManager = (DisplayManager) context .getSystemService(Context.DISPLAY_SERVICE); } public synchronized void start(String path) { if (!started) { mVirtualDisplayThread = new VirtualDisplayThread(720, 1280, 320,path); mVirtualDisplayThread.start(); started = true; } } public synchronized void stop() { if (started) { started = false; mVirtualDisplayThread.quit(); } } public boolean isStarted(){ return started; } private final class VirtualDisplayThread extends Thread { private static final int TIMEOUT_USEC = 1000000; private static final int BIT_RATE = 6000000; private static final int FRAME_RATE = 30; private static final int I_FRAME_INTERVAL = 10; private final int mWidth; private final int mHeight; private final int mDensityDpi; private MediaMuxer muxer; private volatile boolean mQuitting; private boolean mMuxerStarted; public VirtualDisplayThread(int width, int height, int densityDpi,String path) { mWidth = width; mHeight = height; mDensityDpi = densityDpi; try { muxer = new MediaMuxer(path, OutputFormat.MUXER_OUTPUT_MPEG_4); } catch (IOException e) { Log.e("sam", e.getMessage(), e); } } @Override public void run() { MediaFormat format = MediaFormat.createVideoFormat("video/avc", mWidth, mHeight); format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface); format.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE); format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE); format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, I_FRAME_INTERVAL); MediaCodec codec = MediaCodec.createEncoderByType("video/avc"); codec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); Surface surface = codec.createInputSurface(); codec.start(); VirtualDisplay virtualDisplay = mDisplayManager .createVirtualDisplay(DISPLAY_NAME, mWidth, mHeight, mDensityDpi, surface, DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC); if (virtualDisplay != null) { stream(codec); virtualDisplay.release(); } codec.signalEndOfInputStream(); codec.stop(); } public void quit() { mQuitting = true; } private void stream(MediaCodec codec) { BufferInfo info = new BufferInfo(); ByteBuffer[] buffers = null; while (!mQuitting) { int index = codec.dequeueOutputBuffer(info, TIMEOUT_USEC); if (index >= 0) { if (buffers == null) { buffers = codec.getOutputBuffers(); } ByteBuffer buffer = buffers[index]; buffer.limit(info.offset + info.size); buffer.position(info.offset); muxer.writeSampleData(videoTrackIndex, buffer, info); codec.releaseOutputBuffer(index, false); } else if (index == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { if (mMuxerStarted) { throw new RuntimeException("format changed twice"); } MediaFormat newFormat = codec.getOutputFormat(); // now that we have the Magic Goodies, start the muxer videoTrackIndex = muxer.addTrack(newFormat); muxer.start(); mMuxerStarted = true; buffers = null; } else if (index == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { buffers = null; } else if (index == MediaCodec.INFO_TRY_AGAIN_LATER) { Log.e("sam", "Codec dequeue buffer timed out."); } } muxer.stop(); muxer.release(); } } }