Skip to content

Commit

Permalink
Changing codec-profile -> codec-options
Browse files Browse the repository at this point in the history
  • Loading branch information
Tzah Mazuz committed Apr 17, 2020
1 parent eaef1c1 commit c265d63
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 25 deletions.
25 changes: 25 additions & 0 deletions server/src/main/java/com/genymobile/scrcpy/CodecOptions.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.genymobile.scrcpy;

import java.util.HashMap;

public class CodecOptions {
static final String PROFILE_OPTION = "profile";
static final String LEVEL_OPTION = "level";

private HashMap<String, String> options;

CodecOptions(HashMap<String, String> options) {
this.options = options;
}

Object parseValue(String profileOption) {
String value = options.get(profileOption);
switch (profileOption) {
case PROFILE_OPTION:
case LEVEL_OPTION:
return NumberUtils.tryParseInt(value);
default:
return null;
}
}
}
16 changes: 16 additions & 0 deletions server/src/main/java/com/genymobile/scrcpy/NumberUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.genymobile.scrcpy;

public class NumberUtils {

public static int tryParseInt(final String str) {
return tryParseInt(str, 0);
}

public static int tryParseInt(final String str, int defaultValue) {
try {
return Integer.parseInt(str);
} catch (NumberFormatException e) {
return defaultValue;
}
}
}
10 changes: 5 additions & 5 deletions server/src/main/java/com/genymobile/scrcpy/Options.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class Options {
private Rect crop;
private boolean sendFrameMeta; // send PTS so that the client may record properly
private boolean control;
private int codecProfile;
private CodecOptions codecOptions;

public int getMaxSize() {
return maxSize;
Expand Down Expand Up @@ -77,11 +77,11 @@ public void setControl(boolean control) {
this.control = control;
}

public int getCodecProfile() {
return codecProfile;
public CodecOptions getCodecOptions() {
return codecOptions;
}

public void setCodecProfile(int codecProfile) {
this.codecProfile = codecProfile;
public void setCodecOptions(CodecOptions codecOptions) {
this.codecOptions = codecOptions;
}
}
41 changes: 24 additions & 17 deletions server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicBoolean;

import static android.media.MediaFormat.MIMETYPE_VIDEO_AVC;

public class ScreenEncoder implements Device.RotationListener {

private static final int DEFAULT_I_FRAME_INTERVAL = 10; // seconds
Expand All @@ -30,21 +32,21 @@ public class ScreenEncoder implements Device.RotationListener {
private int maxFps;
private int lockedVideoOrientation;
private int iFrameInterval;
private int codecProfile;
private boolean sendFrameMeta;
private long ptsOrigin;
private CodecOptions codecOptions;

public ScreenEncoder(boolean sendFrameMeta, int bitRate, int maxFps, int lockedVideoOrientation, int codecProfile, int iFrameInterval) {
public ScreenEncoder(boolean sendFrameMeta, int bitRate, int maxFps, int lockedVideoOrientation, CodecOptions codecOptions, int iFrameInterval) {
this.sendFrameMeta = sendFrameMeta;
this.bitRate = bitRate;
this.maxFps = maxFps;
this.lockedVideoOrientation = lockedVideoOrientation;
this.codecProfile = codecProfile;
this.codecOptions = codecOptions;
this.iFrameInterval = iFrameInterval;
}

public ScreenEncoder(boolean sendFrameMeta, int bitRate, int maxFps, int lockedVideoOrientation, int codecProfile) {
this(sendFrameMeta, bitRate, maxFps, lockedVideoOrientation, codecProfile, DEFAULT_I_FRAME_INTERVAL);
public ScreenEncoder(boolean sendFrameMeta, int bitRate, int maxFps, int lockedVideoOrientation, CodecOptions codecOptions) {
this(sendFrameMeta, bitRate, maxFps, lockedVideoOrientation, codecOptions, DEFAULT_I_FRAME_INTERVAL);
}

@Override
Expand Down Expand Up @@ -142,30 +144,35 @@ private void writeFrameMeta(FileDescriptor fd, MediaCodec.BufferInfo bufferInfo,
IO.writeFully(fd, headerBuffer);
}

private void setCodecProfile(MediaCodec codec, MediaFormat format) throws IOException {
if(codecProfile == 0) return;
int level = 0;
for (MediaCodecInfo.CodecProfileLevel profileLevel : codec.getCodecInfo().getCapabilitiesForType("video/avc").profileLevels) {
if(profileLevel.profile == codecProfile) {
private void setCodecProfile(MediaCodec codec, MediaFormat format) {
int profile = (int)codecOptions.parseValue(CodecOptions.PROFILE_OPTION);
int level = (int)codecOptions.parseValue(CodecOptions.LEVEL_OPTION);
if(profile == 0) return;
for (MediaCodecInfo.CodecProfileLevel profileLevel : codec.getCodecInfo().getCapabilitiesForType(MIMETYPE_VIDEO_AVC).profileLevels) {
if(profileLevel.profile == profile) {
level = Math.max(level, profileLevel.level);
}
}
if(level == 0) throw new IOException("Device doesn't support the requested codec profile.");
// Profile (SDK Level 21) and Level (SDK Level 23).
format.setInteger(MediaFormat.KEY_PROFILE, codecProfile);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
format.setInteger(MediaFormat.KEY_LEVEL, level);
if(level == 0) {
Ln.w("Device doesn't support the requested codec profile.\n" +
"Profile and level will be chosen automatically.");
} else {
// Profile (SDK Level 21) and Level (SDK Level 23).
format.setInteger(MediaFormat.KEY_PROFILE, profile);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
format.setInteger(MediaFormat.KEY_LEVEL, level);
}
}
}

private static MediaCodec createCodec() throws IOException {
return MediaCodec.createEncoderByType("video/avc");
return MediaCodec.createEncoderByType(MIMETYPE_VIDEO_AVC);
}

@SuppressWarnings("checkstyle:MagicNumber")
private static MediaFormat createFormat(int bitRate, int maxFps, int iFrameInterval) {
MediaFormat format = new MediaFormat();
format.setString(MediaFormat.KEY_MIME, "video/avc");
format.setString(MediaFormat.KEY_MIME, MIMETYPE_VIDEO_AVC);
format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
// must be present to configure the encoder, but does not impact the actual frame rate, which is variable
format.setInteger(MediaFormat.KEY_FRAME_RATE, 60);
Expand Down
19 changes: 16 additions & 3 deletions server/src/main/java/com/genymobile/scrcpy/Server.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import java.io.File;
import java.io.IOException;
import java.util.HashMap;

public final class Server {

Expand All @@ -20,7 +21,7 @@ private static void scrcpy(Options options) throws IOException {
boolean tunnelForward = options.isTunnelForward();
try (DesktopConnection connection = DesktopConnection.open(device, tunnelForward)) {
ScreenEncoder screenEncoder = new ScreenEncoder(options.getSendFrameMeta(), options.getBitRate(), options.getMaxFps(),
options.getLockedVideoOrientation(), options.getCodecProfile());
options.getLockedVideoOrientation(), options.getCodecOptions());

if (options.getControl()) {
Controller controller = new Controller(device, connection);
Expand Down Expand Up @@ -98,8 +99,8 @@ private static Options createOptions(String... args) {
int lockedVideoOrientation = Integer.parseInt(args[4]);
options.setLockedVideoOrientation(lockedVideoOrientation);

int codecProfile = Integer.parseInt(args[5]);
options.setCodecProfile(codecProfile);
CodecOptions codecOptions = parseCodecOptions(args[5]);
options.setCodecOptions(codecOptions);

// use "adb forward" instead of "adb tunnel"? (so the server must listen)
boolean tunnelForward = Boolean.parseBoolean(args[6]);
Expand Down Expand Up @@ -134,6 +135,18 @@ private static Rect parseCrop(String crop) {
return new Rect(x, y, x + width, y + height);
}

private static CodecOptions parseCodecOptions(String codecOptions) {
HashMap<String, String> codecOptionsMap = new HashMap<>();
if (!"-".equals(codecOptions)) {
String[] pairs = codecOptions.split(",");
for (String pair : pairs) {
String[] option = pair.split("=");
codecOptionsMap.put(option[0], option.length > 1 ? option[1] : null);
}
}
return new CodecOptions(codecOptionsMap);
}

private static void unlinkSelf() {
try {
new File(SERVER_PATH).delete();
Expand Down

0 comments on commit c265d63

Please sign in to comment.