Skip to content

Commit 858468c

Browse files
authored
Improve video recording quality and simplify native library loading (#561)
* fix: improve video recording quality and simplify native library loading - CGEFrameRecorder: Add adaptive bitrate calculation based on resolution - VideoQuality enum (LOW/MEDIUM/HIGH/ULTRA) - calculateBitrate() method for resolution-aware bitrate - New API: startRecording(fps, filename, quality) - Improves 1080p quality from 1.65Mbps to 10Mbps - Maintains backward compatibility with original startRecording(fps, filename) - NativeLibraryLoader: Add error logging with device info - Wrap library loading in try-catch to log device details on failure - Helps debugging on specific devices/Android versions - cgeImageHandlerAndroid.cpp: Improve error message formatting - Log actual image dimensions on validation errors Build verified: all tests passing * Upgrade Gradle and Android build tools versions - Update Gradle version from 8.11.1 to 8.13 - Update Android Gradle plugin from 8.10.1 to 8.13.2 - Update compileSdkVersion to 35 for Android 15 support with 16KB page sizes
1 parent f608d95 commit 858468c

6 files changed

Lines changed: 176 additions & 13 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ obj/
1212
build/
1313
library/.cxx/
1414
.kotlin/
15+
tasks/
1516

1617
# CodeQL
1718
_codeql_detected_source_root

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ buildscript {
99
google()
1010
}
1111
dependencies {
12-
classpath 'com.android.tools.build:gradle:8.10.1'
12+
classpath 'com.android.tools.build:gradle:8.13.2'
1313
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
1414

1515
// NOTE: Do not place your application dependencies here; they belong
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#Thu Jan 05 14:44:46 CST 2023
22
distributionBase=GRADLE_USER_HOME
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
44
distributionPath=wrapper/dists
55
zipStorePath=wrapper/dists
66
zipStoreBase=GRADLE_USER_HOME

library/src/main/java/org/wysaid/nativePort/CGEFrameRecorder.java

Lines changed: 149 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package org.wysaid.nativePort;
22

3+
import android.util.Log;
4+
5+
import org.wysaid.common.Common;
6+
37
import java.nio.ShortBuffer;
48

59
/**
@@ -13,23 +17,167 @@ public class CGEFrameRecorder extends CGEFrameRenderer {
1317
NativeLibraryLoader.load();
1418
}
1519

20+
/**
21+
* Video quality presets for adaptive bitrate calculation
22+
*/
23+
public enum VideoQuality {
24+
LOW, // Suitable for network sharing, smaller file size
25+
MEDIUM, // Balance between quality and size
26+
HIGH, // High quality (recommended for most use cases)
27+
ULTRA // Ultra high quality (near lossless)
28+
}
29+
30+
// Track output dimensions for bitrate calculation
31+
private int mOutputWidth = 0;
32+
private int mOutputHeight = 0;
33+
1634
public CGEFrameRecorder() {
1735
super(0); //avoid multiple creation.
1836
mNativeAddress = nativeCreateRecorder();
1937
}
2038

39+
@Override
40+
public boolean init(int srcWidth, int srcHeight, int dstWidth, int dstHeight) {
41+
boolean result = super.init(srcWidth, srcHeight, dstWidth, dstHeight);
42+
if (result) {
43+
mOutputWidth = dstWidth;
44+
mOutputHeight = dstHeight;
45+
}
46+
return result;
47+
}
48+
49+
/**
50+
* Calculate recommended bitrate based on resolution and quality preset
51+
* @param width video width
52+
* @param height video height
53+
* @param quality quality preset
54+
* @return recommended bitrate in bps
55+
*/
56+
private int calculateBitrate(int width, int height, VideoQuality quality) {
57+
int baseBitrate = getBaseBitrate(width, height);
58+
59+
// Adjust based on quality setting
60+
switch (quality) {
61+
case LOW:
62+
return (int) (baseBitrate * 0.5); // 50%
63+
case MEDIUM:
64+
return (int) (baseBitrate * 0.75); // 75%
65+
case HIGH:
66+
return baseBitrate; // 100%
67+
case ULTRA:
68+
return (int) (baseBitrate * 1.5); // 150%
69+
default:
70+
return baseBitrate;
71+
}
72+
}
73+
74+
private static int getBaseBitrate(int width, int height) {
75+
int pixels = width * height;
76+
int baseBitrate;
77+
78+
// Base bitrate based on resolution
79+
if (pixels <= 640 * 480) { // VGA (~0.3 MP)
80+
baseBitrate = 2_000_000; // 2 Mbps
81+
} else if (pixels <= 1280 * 720) { // 720p (~0.9 MP)
82+
baseBitrate = 5_000_000; // 5 Mbps
83+
} else if (pixels <= 1920 * 1080) { // 1080p (~2 MP)
84+
baseBitrate = 10_000_000; // 10 Mbps
85+
} else if (pixels <= 2560 * 1440) { // 1440p (~3.7 MP)
86+
baseBitrate = 16_000_000; // 16 Mbps
87+
} else { // 4K+ (8 MP+)
88+
baseBitrate = 40_000_000; // 40 Mbps
89+
}
90+
return baseBitrate;
91+
}
92+
2193
///////////////// Video recording related ////////////////////
2294

95+
/**
96+
* Start recording with adaptive bitrate based on resolution (HIGH quality)
97+
* For custom quality, use {@link #startRecording(int, String, VideoQuality)}
98+
* @param fps frames per second
99+
* @param filename output file path
100+
* @return true if recording started successfully
101+
* @deprecated Default bitrate changed from 1.65 Mbps to adaptive HIGH (~10 Mbps for 1080p).
102+
* Use {@link #startRecording(int, String, VideoQuality)} for explicit control.
103+
*/
104+
@Deprecated
23105
public boolean startRecording(int fps, String filename) {
24-
return startRecording(fps, 1650000, filename);
106+
Log.w(Common.LOG_TAG,
107+
"startRecording: Using HIGH quality preset (~10Mbps for 1080p). " +
108+
"Previous default was 1.65Mbps. Use startRecording(fps, filename, VideoQuality) for explicit control.");
109+
return startRecording(fps, filename, VideoQuality.HIGH);
110+
}
111+
112+
/**
113+
* Start recording with specified quality preset (recommended)
114+
* @param fps frames per second
115+
* @param filename output file path
116+
* @param quality video quality preset (LOW/MEDIUM/HIGH/ULTRA)
117+
* @return true if recording started successfully
118+
*/
119+
public boolean startRecording(int fps, String filename, VideoQuality quality) {
120+
// Use default 1080p if dimensions not set
121+
int width = (mOutputWidth > 0) ? mOutputWidth : 1920;
122+
int height = (mOutputHeight > 0) ? mOutputHeight : 1080;
123+
124+
if (mOutputWidth <= 0 || mOutputHeight <= 0) {
125+
Log.w(Common.LOG_TAG,
126+
String.format("startRecording: Output dimensions not initialized, defaulting to %dx%d. " +
127+
"Call init() or use startRecording(fps, filename, width, height, quality) for accurate bitrate.",
128+
width, height));
129+
}
130+
131+
int bitrate = calculateBitrate(width, height, quality);
132+
return startRecording(fps, bitrate, filename);
25133
}
26134

135+
/**
136+
* Start recording with specified quality and explicit dimensions
137+
* @param fps frames per second
138+
* @param filename output file path
139+
* @param width video width (for bitrate calculation)
140+
* @param height video height (for bitrate calculation)
141+
* @param quality video quality preset
142+
* @return true if recording started successfully
143+
*/
144+
public boolean startRecording(int fps, String filename, int width, int height, VideoQuality quality) {
145+
int bitrate = calculateBitrate(width, height, quality);
146+
return startRecording(fps, bitrate, filename);
147+
}
148+
149+
/**
150+
* Start recording with manual bitrate specification (advanced usage)
151+
* @param fps frames per second
152+
* @param bitRate bitrate in bps
153+
* @param filename output file path
154+
* @return true if recording started successfully
155+
*/
27156
public boolean startRecording(int fps, int bitRate, String filename) {
28157
if(mNativeAddress != 0)
29158
return nativeStartRecording(mNativeAddress, fps, filename, bitRate);
30159
return false;
31160
}
32161

162+
/**
163+
* Get recommended bitrate for specified quality
164+
* @param quality video quality preset
165+
* @return recommended bitrate in bps
166+
*/
167+
public int getRecommendedBitrate(VideoQuality quality) {
168+
int width = (mOutputWidth > 0) ? mOutputWidth : 1920;
169+
int height = (mOutputHeight > 0) ? mOutputHeight : 1080;
170+
171+
if (mOutputWidth <= 0 || mOutputHeight <= 0) {
172+
Log.w(Common.LOG_TAG,
173+
String.format("getRecommendedBitrate: Output dimensions not initialized, using default %dx%d. " +
174+
"Call init() first for resolution-specific bitrate.",
175+
width, height));
176+
}
177+
178+
return calculateBitrate(width, height, quality);
179+
}
180+
33181
public boolean isRecordingStarted() {
34182
if(mNativeAddress != 0)
35183
return nativeIsRecordingStarted(mNativeAddress);

library/src/main/java/org/wysaid/nativePort/NativeLibraryLoader.java

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package org.wysaid.nativePort;
22

3+
import android.os.Build;
4+
import android.util.Log;
5+
6+
import org.wysaid.common.Common;
37
import org.wysaid.library.BuildConfig;
48

59
/**
@@ -8,16 +12,26 @@
812
public class NativeLibraryLoader {
913

1014
public static void load() {
11-
12-
if (BuildConfig.CGE_USE_VIDEO_MODULE) {
13-
System.loadLibrary("ffmpeg");
14-
}
15-
System.loadLibrary("CGE");
16-
System.loadLibrary("CGEExt");
17-
if (BuildConfig.CGE_USE_VIDEO_MODULE) {
18-
CGEFFmpegNativeLibrary.avRegisterAll();
15+
try {
16+
if (BuildConfig.CGE_USE_VIDEO_MODULE) {
17+
System.loadLibrary("ffmpeg");
18+
}
19+
System.loadLibrary("CGE");
20+
System.loadLibrary("CGEExt");
21+
if (BuildConfig.CGE_USE_VIDEO_MODULE) {
22+
CGEFFmpegNativeLibrary.avRegisterAll();
23+
}
24+
onLoad();
25+
} catch (UnsatisfiedLinkError e) {
26+
// Log device info for debugging
27+
String abi = (Build.SUPPORTED_ABIS != null && Build.SUPPORTED_ABIS.length > 0)
28+
? Build.SUPPORTED_ABIS[0] : "unknown";
29+
Log.e(Common.LOG_TAG, String.format(
30+
"Failed to load native libraries on %s %s (Android %s, ABI: %s)",
31+
Build.MANUFACTURER, Build.MODEL, Build.VERSION.RELEASE, abi
32+
), e);
33+
throw e;
1934
}
20-
onLoad();
2135
}
2236

2337
static native void onLoad();

library/src/main/jni/interface/cgeImageHandlerAndroid.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ jobject CGEImageHandlerAndroid::getResultBitmap(JNIEnv* env)
9797
{
9898
if(m_dstImageSize.width <= 0 || m_dstImageSize.height <= 0)
9999
{
100-
CGE_LOG_ERROR("Invalid image size!");
100+
CGE_LOG_ERROR("Invalid image size: %dx%d", m_dstImageSize.width, m_dstImageSize.height);
101101
return nullptr;
102102
}
103103

0 commit comments

Comments
 (0)