Skip to content

Commit 803b22a

Browse files
authored
Merge pull request #128 from abrenoch/development
Development
2 parents d412636 + ecc5656 commit 803b22a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+1364
-2806
lines changed

.idea/misc.xml

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

CHANGELOG.md

+20
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,23 @@
1+
## [v0.5-beta]
2+
### Changes
3+
- Added the ability to send only the average color of the screen
4+
- French translation
5+
- Norwegian translation
6+
- Czech translation
7+
- German translation
8+
- Dutch translation
9+
- Partial Russian translation
10+
- Partial Spanish translation
11+
- Removed openGL grabber option
12+
- Added toggle grabber activity shortcut
13+
- LEDs will now be cleared when rebooting or shutting down
14+
15+
### Fixed
16+
- Lights now clear (if running) when shutting down
17+
- Assertion bug in TV settings
18+
- Possible null intent when starting grabber
19+
- OOM bug
20+
121
## [v0.4-alpha]
222
### Changes
323
- Start grabber on device boot

build.gradle

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
// Top-level build file where you can add configuration options common to all sub-projects/modules.
22

33
buildscript {
4-
ext.kotlin_version = '1.2.41'
5-
ext.protobufVersion = "0.8.3"
4+
ext.kotlin_version = '1.2.71'
5+
ext.protobufVersion = "0.8.6"
66

77
repositories {
88
google()
99
mavenCentral()
1010
jcenter()
1111
}
1212
dependencies {
13-
classpath 'com.android.tools.build:gradle:3.1.2'
13+
classpath 'com.android.tools.build:gradle:3.2.1'
1414
classpath "com.google.protobuf:protobuf-gradle-plugin:$protobufVersion"
1515
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
1616
// NOTE: Do not place your application dependencies here; they belong

common/build.gradle

+3-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ buildscript {
1212
}
1313

1414
dependencies {
15-
classpath 'com.android.tools.build:gradle:3.1.2'
16-
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.3'
15+
classpath 'com.android.tools.build:gradle:3.2.1'
16+
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.6'
1717
}
1818
}
1919

@@ -31,6 +31,7 @@ sourceSets {
3131
android {
3232
compileSdkVersion 26
3333
defaultConfig {
34+
minSdkVersion 19
3435
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
3536
vectorDrawables.useSupportLibrary = true
3637
multiDexEnabled true

common/src/main/java/com/abrenoch/hyperiongrabber/common/HyperionNotification.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public class HyperionNotification {
2424
HyperionNotification (Context ctx, NotificationManager manager) {
2525
mNotificationManager = manager;
2626
mContext = ctx;
27-
NOTIFICATION_TITLE = mContext.getString(R.string.notification_title);
27+
NOTIFICATION_TITLE = mContext.getString(R.string.app_name);
2828
NOTIFICATION_DESCRIPTION = mContext.getString(R.string.notification_description);
2929
NOTIFICATION_CHANNEL_LABEL = mContext.getString(R.string.notification_channel_label);
3030
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

common/src/main/java/com/abrenoch/hyperiongrabber/common/HyperionScreenEncoder.java

+85-16
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import android.util.Log;
1515

1616
import com.abrenoch.hyperiongrabber.common.network.HyperionThread;
17+
import com.abrenoch.hyperiongrabber.common.util.BorderProcessor;
1718
import com.abrenoch.hyperiongrabber.common.util.HyperionGrabberOptions;
1819

1920
import java.io.ByteArrayOutputStream;
@@ -42,6 +43,8 @@ public class HyperionScreenEncoder extends HyperionScreenEncoderBase {
4243
@TargetApi(Build.VERSION_CODES.M)
4344
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
4445
private void prepare() throws MediaCodec.CodecException {
46+
if (DEBUG) Log.d(TAG, "Preparing encoder");
47+
4548
mVirtualDisplay = mMediaProjection.createVirtualDisplay(
4649
TAG,
4750
getGrabberWidth(), getGrabberHeight(), mDensity,
@@ -113,6 +116,7 @@ public void onStopped() {
113116

114117
@RequiresApi(api = Build.VERSION_CODES.KITKAT_WATCH)
115118
private void setImageReader() {
119+
if (DEBUG) Log.d(TAG, "Setting image reader " + String.valueOf(isCapturing()));
116120
mImageReader = ImageReader.newInstance(getGrabberWidth(), getGrabberHeight(),
117121
PixelFormat.RGBA_8888, MAX_IMAGE_READER_IMAGES);
118122
mImageReader.setOnImageAvailableListener(imageAvailableListener, mHandler);
@@ -132,7 +136,7 @@ public void onImageAvailable(ImageReader reader) {
132136
long now = System.nanoTime();
133137
Image img = reader.acquireLatestImage();
134138
if (img != null && now - lastFrame >= min_nano_time) {
135-
mListener.sendFrame(savePixels(img), getGrabberWidth(), getGrabberHeight());
139+
sendImage(img);
136140
img.close();
137141
lastFrame = now;
138142
} else if (img != null) {
@@ -145,28 +149,93 @@ public void onImageAvailable(ImageReader reader) {
145149
}
146150
};
147151

148-
private byte[] savePixels(Image image) throws IllegalStateException {
149-
Image.Plane plane = image.getPlanes()[0];
150-
ByteBuffer buffer = plane.getBuffer();
152+
private byte[] getPixels(ByteBuffer buffer, int width, int height, int rowStride,
153+
int pixelStride, int firstX, int firstY){
154+
int rowPadding = rowStride - width * pixelStride;
155+
int offset = 0;
151156

152-
int width = image.getWidth();
153-
int height = image.getHeight();
154-
int pixelStride = plane.getPixelStride();
155-
int rowPadding = plane.getRowStride() - width * pixelStride;
157+
ByteArrayOutputStream bao = new ByteArrayOutputStream(
158+
(width - firstX * 2) * (height - firstY * 2) * 3
159+
);
160+
161+
for (int y = 0, compareHeight = height - firstY - 1; y < height; y++, offset += rowPadding) {
162+
if (y < firstY || y > compareHeight) {
163+
offset += width * pixelStride;
164+
continue;
165+
}
166+
for (int x = 0, compareWidth = width - firstX - 1; x < width; x++, offset += pixelStride) {
167+
if (x < firstX || x > compareWidth) continue;
168+
bao.write(buffer.get(offset) & 0xff); // R
169+
bao.write(buffer.get(offset + 1) & 0xff); // G
170+
bao.write(buffer.get(offset + 2) & 0xff); // B
171+
}
172+
}
156173

157-
ByteArrayOutputStream bao = new ByteArrayOutputStream(width * height * 3);
174+
return bao.toByteArray();
175+
}
158176

177+
private byte[] getAverageColor(ByteBuffer buffer, int width, int height, int rowStride,
178+
int pixelStride, int firstX, int firstY) {
179+
long totalRed = 0, totalGreen = 0, totalBlue = 0;
180+
int rowPadding = rowStride - width * pixelStride;
181+
int pixelCount = 0;
159182
int offset = 0;
160-
for (int i = 0; i < height; i++) {
161-
for (int j = 0; j < width; j++) {
162-
bao.write(buffer.get(offset)); // R
163-
bao.write(buffer.get(offset + 1)); // G
164-
bao.write(buffer.get(offset + 2)); // B
165-
offset += pixelStride;
183+
184+
ByteArrayOutputStream bao = new ByteArrayOutputStream(3);
185+
186+
for (int y = 0, compareHeight = height - firstY - 1; y < height; y++, offset += rowPadding) {
187+
if (y < firstY || y > compareHeight) {
188+
offset += width * pixelStride;
189+
continue;
190+
}
191+
for (int x = 0, compareWidth = width - firstX - 1; x < width; x++, offset += pixelStride) {
192+
if (x < firstX || x > compareWidth) continue;
193+
totalRed += buffer.get(offset) & 0xff; // R
194+
totalGreen += buffer.get(offset + 1) & 0xff; // G
195+
totalBlue += buffer.get(offset + 2) & 0xff; // B
196+
pixelCount++;
166197
}
167-
offset += rowPadding;
168198
}
169199

200+
bao.write((int) totalRed / pixelCount);
201+
bao.write((int) totalGreen / pixelCount);
202+
bao.write((int) totalBlue / pixelCount);
203+
170204
return bao.toByteArray();
171205
}
206+
207+
private void sendImage(Image img) {
208+
Image.Plane plane = img.getPlanes()[0];
209+
ByteBuffer buffer = plane.getBuffer();
210+
211+
int width = img.getWidth();
212+
int height = img.getHeight();
213+
int pixelStride = plane.getPixelStride();
214+
int rowStride = plane.getRowStride();
215+
int firstX = 0;
216+
int firstY = 0;
217+
218+
if (mRemoveBorders || mAvgColor) {
219+
mBorderProcessor.parseBorder(buffer, width, height, rowStride, pixelStride);
220+
BorderProcessor.BorderObject border = mBorderProcessor.getCurrentBorder();
221+
if (border != null && border.isKnown()) {
222+
firstX = border.getHorizontalBorderIndex();
223+
firstY = border.getVerticalBorderIndex();
224+
}
225+
}
226+
227+
if (mAvgColor) {
228+
mListener.sendFrame(
229+
getAverageColor(buffer, width, height, rowStride, pixelStride, firstX, firstY),
230+
1,
231+
1
232+
);
233+
} else {
234+
mListener.sendFrame(
235+
getPixels(buffer, width, height, rowStride, pixelStride, firstX, firstY),
236+
width - firstX * 2,
237+
height - firstY * 2
238+
);
239+
}
240+
}
172241
}

common/src/main/java/com/abrenoch/hyperiongrabber/common/HyperionScreenEncoderBase.java

+31-7
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,26 @@
77
import android.util.Log;
88

99
import com.abrenoch.hyperiongrabber.common.network.HyperionThread;
10+
import com.abrenoch.hyperiongrabber.common.util.BorderProcessor;
1011
import com.abrenoch.hyperiongrabber.common.util.HyperionGrabberOptions;
1112

1213
public class HyperionScreenEncoderBase {
13-
private static final boolean DEBUG = false;
14+
static final boolean DEBUG = false;
1415
private static final String TAG = "ScreenEncoderBase";
16+
private final int CLEAR_COMMAND_DELAY_MS = 100;
1517
private final int INIT_ORIENTATION;
16-
int mWidthScaled;
17-
int mFrameRate;
18-
int mHeightScaled;
19-
int mDensity;
20-
int mCurrentOrientation;
21-
Handler mHandler;
2218

19+
final boolean mRemoveBorders = false; // enables detecting borders for standard grabbing - disabled for now
20+
final boolean mAvgColor;
21+
final int mWidthScaled;
22+
final int mFrameRate;
23+
final int mHeightScaled;
24+
final int mDensity;
2325
boolean mIsCapturing = false;
26+
int mCurrentOrientation;
27+
28+
BorderProcessor mBorderProcessor;
29+
Handler mHandler;
2430
MediaProjection mMediaProjection;
2531
HyperionThread.HyperionThreadListener mListener;
2632

@@ -33,6 +39,10 @@ public class HyperionScreenEncoderBase {
3339
mMediaProjection = projection;
3440
mDensity = density;
3541
mFrameRate = options.getFrameRate();
42+
mAvgColor = options.useAverageColor();
43+
44+
int blackThreshold = options.getBlackThreshold();
45+
mBorderProcessor = new BorderProcessor(blackThreshold);
3646

3747
mCurrentOrientation = INIT_ORIENTATION = width > height ? Configuration.ORIENTATION_LANDSCAPE :
3848
Configuration.ORIENTATION_PORTRAIT;
@@ -42,6 +52,8 @@ public class HyperionScreenEncoderBase {
4252
Log.d(TAG, "Frame Rate: " + String.valueOf(mFrameRate));
4353
Log.d(TAG, "Original Width: " + String.valueOf(width));
4454
Log.d(TAG, "Original Height: " + String.valueOf(height));
55+
Log.d(TAG, "Average Color Only: " + String.valueOf(mAvgColor));
56+
Log.d(TAG, "Black Pixel Threshold: " + String.valueOf(blackThreshold));
4557
}
4658

4759
// find the common divisor for width & height best fit for the LED count (defined in options)
@@ -66,13 +78,25 @@ public class HyperionScreenEncoderBase {
6678

6779
private Runnable clearAndDisconnectRunner = new Runnable() {
6880
public void run() {
81+
if (DEBUG) Log.d(TAG, "Clearing LEDs and disconnecting");
82+
try {
83+
Thread.sleep(CLEAR_COMMAND_DELAY_MS);
84+
} catch (InterruptedException e) {
85+
e.printStackTrace();
86+
}
6987
mListener.clear();
7088
mListener.disconnect();
7189
}
7290
};
7391

7492
private Runnable clearLightsRunner = new Runnable() {
7593
public void run() {
94+
if (DEBUG) Log.d(TAG, "Clearing LEDs");
95+
try {
96+
Thread.sleep(CLEAR_COMMAND_DELAY_MS);
97+
} catch (InterruptedException e) {
98+
e.printStackTrace();
99+
}
76100
mListener.clear();
77101
}
78102
};

0 commit comments

Comments
 (0)