forward --list list all forward socket connectionsforward [--no-rebind] LOCAL REMOTEforward socket connection using:tcp:<port> (<local> may be "tcp:0" to pick any open port)localabstract:<unix domain socket name>localreserved:<unix domain socket name>localfilesystem:<unix domain socket name>dev:<character device name>jdwp:<process pid> (remote only)forward --remove LOCAL remove specific forward socket connectionforward --remove-all remove all forward socket connections
package com.cry.cry.appprocessdemo;import android.graphics.Rect;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.os.IBinder;
import android.view.Surface;import com.cry.cry.appprocessdemo.refect.SurfaceControl;import java.io.FileDescriptor;
import java.io.IOException;
import java.nio.ByteBuffer;public class ScreenRecorder {private static final int DEFAULT_FRAME_RATE = 60; // fpsprivate static final int DEFAULT_I_FRAME_INTERVAL = 10; // secondsprivate static final int DEFAULT_BIT_RATE = 8000000; // 8Mbpsprivate static final int DEFAULT_TIME_OUT = 2 * 1000; // 2sprivate static final int REPEAT_FRAME_DELAY = 6; // repeat after 6 framesprivate static final int MICROSECONDS_IN_ONE_SECOND = 1_000_000;private static final int NO_PTS = -1;private boolean sendFrameMeta = false;private final ByteBuffer headerBuffer = ByteBuffer.allocate(12);private long ptsOrigin;private volatile boolean stop;private MediaCodec encoder;public void setStop(boolean stop) {this.stop = stop;
// encoder.signalEndOfInputStream();}//進行錄制的循環(huán),錄制得到的數(shù)據(jù),都寫到fd當(dāng)中public void record(int width, int height, FileDescriptor fd) {//對MediaCodec進行配置boolean alive;try {do {MediaFormat mediaFormat = createMediaFormat(DEFAULT_BIT_RATE, DEFAULT_FRAME_RATE, DEFAULT_I_FRAME_INTERVAL);mediaFormat.setInteger(MediaFormat.KEY_WIDTH, width);mediaFormat.setInteger(MediaFormat.KEY_HEIGHT, height);encoder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);//輸入輸出的surface 這里是沒有encoder.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);Surface inputSurface = encoder.createInputSurface();IBinder surfaceClient = setDisplaySurface(width, height, inputSurface);encoder.start();try {alive = encode(encoder, fd);alive = alive && !stop;System.out.println("alive =" + alive + ", stop=" + stop);} finally {System.out.println("encoder.stop");//為什么調(diào)用stop會block主呢?
// encoder.stop();System.out.println("destroyDisplaySurface");destroyDisplaySurface(surfaceClient);System.out.println("encoder release");encoder.release();System.out.println("inputSurface release");inputSurface.release();System.out.println("end");}} while (alive);} catch (IOException e) {e.printStackTrace();}System.out.println("end record");}//創(chuàng)建錄制的Surfaceprivate IBinder setDisplaySurface(int width, int height, Surface inputSurface) {Rect deviceRect = new Rect(0, 0, width, height);Rect displayRect = new Rect(0, 0, width, height);IBinder surfaceClient = SurfaceControl.createDisplay("recorder", false);//設(shè)置和配置截屏的SurfaceSurfaceControl.openTransaction();try {SurfaceControl.setDisplaySurface(surfaceClient, inputSurface);SurfaceControl.setDisplayProjection(surfaceClient, 0, deviceRect, displayRect);SurfaceControl.setDisplayLayerStack(surfaceClient, 0);} finally {SurfaceControl.closeTransaction();}return surfaceClient;}private void destroyDisplaySurface(IBinder surfaceClient) {SurfaceControl.destroyDisplay(surfaceClient);}//創(chuàng)建MediaFormatprivate MediaFormat createMediaFormat(int bitRate, int frameRate, int iFrameInterval) {MediaFormat mediaFormat = new MediaFormat();mediaFormat.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_VIDEO_AVC);mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, iFrameInterval);mediaFormat.setLong(MediaFormat.KEY_REPEAT_PREVIOUS_FRAME_AFTER, MICROSECONDS_IN_ONE_SECOND * REPEAT_FRAME_DELAY / frameRate);//usreturn mediaFormat;}//進行encodeprivate boolean encode(MediaCodec codec, FileDescriptor fd) throws IOException {System.out.println("encode");boolean eof = false;MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();while (!eof) {System.out.println("dequeueOutputBuffer outputBufferId before");int outputBufferId = codec.dequeueOutputBuffer(bufferInfo, DEFAULT_TIME_OUT);System.out.println("dequeueOutputBuffer outputBufferId =" + outputBufferId);eof = (bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;System.out.println("encode eof =" + eof);try {
// if (consumeRotationChange()) {
// // must restart encoding with new size
// break;
// }if (stop) {// must restart encoding with new sizebreak;}//將得到的數(shù)據(jù),都發(fā)送給fdif (outputBufferId >= 0) {ByteBuffer codecBuffer = codec.getOutputBuffer(outputBufferId);System.out.println("dequeueOutputBuffer getOutputBuffer");if (sendFrameMeta) {writeFrameMeta(fd, bufferInfo, codecBuffer.remaining());}IO.writeFully(fd, codecBuffer);System.out.println("writeFully");}} finally {if (outputBufferId >= 0) {codec.releaseOutputBuffer(outputBufferId, false);System.out.println("releaseOutputBuffer");}}}return !eof;}private void writeFrameMeta(FileDescriptor fd, MediaCodec.BufferInfo bufferInfo, int packetSize) throws IOException {headerBuffer.clear();long pts;if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {pts = NO_PTS; // non-media data packet} else {if (ptsOrigin == 0) {ptsOrigin = bufferInfo.presentationTimeUs;}pts = bufferInfo.presentationTimeUs - ptsOrigin;}headerBuffer.putLong(pts);headerBuffer.putInt(packetSize);headerBuffer.flip();IO.writeFully(fd, headerBuffer);}
}
socket發(fā)送 調(diào)用了Os.write方法進行發(fā)送。
public static void writeFully(FileDescriptor fd, ByteBuffer from) throws IOException {// ByteBuffer position is not updated as expected by Os.write() on old Android versions, so// count the remaining bytes manually.// See <https://github.com/Genymobile/scrcpy/issues/291>.int remaining = from.remaining();while (remaining > 0) {try {int w = Os.write(fd, from);if (BuildConfig.DEBUG && w < 0) {// w should not be negative, since an exception is thrown on errorSystem.out.println("Os.write() returned a negative value (" + w + ")");throw new AssertionError("Os.write() returned a negative value (" + w + ")");}remaining -= w;} catch (ErrnoException e) {e.printStackTrace();if (e.errno != OsConstants.EINTR) {throw new IOException(e);}}}}