-
-
Notifications
You must be signed in to change notification settings - Fork 10.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Audio not working on Vivo(Iqoo) Android 13 device #3805
Comments
Yes, unfortunately it doesn't work on some vivo devices: #3757 (comment) #3791 (comment) |
Following the discussion from #3791 (because here is the right place for this issue). I asked to pull the following
Here are the links you provided:
And the list of available jarfiles in adb shell ls /system/framework
The class public class VivoAudioRecordImpl implements IVivoAudioRecord {
// …
private Context mContext;
// …
public VivoAudioRecordImpl() {
Application currentApplication = ActivityThread.currentApplication();
this.mContext = currentApplication;
if (currentApplication != null) {
this.mAudioFeatures = new AudioFeatures(this.mContext, (String) null, (Object) null);
} else {
this.mAudioFeatures = null;
}
}
// …
public String isSupportSubMixRecording() {
AudioFeatures.TagParameters tp = new AudioFeatures.TagParameters("vivo_remote_support");
String mPackageName = this.mContext.getOpPackageName();
String PackSha256 = getSignInfo(this.mContext, mPackageName, SHA256);
tp.put(VivoPermissionManager.ACTION_KEY_PACKAGE, mPackageName);
tp.put("component", PackSha256);
tp.put(Calendar.CalendarAlertsColumns.STATE, VRequest.COMMAND_QUERY);
String ret = new AudioFeatures.TagParameters(this.mAudioFeatures.getAudioFeature(tp.toString(), this)).get(Calendar.CalendarAlertsColumns.STATE);
VLog.w(TAG, "isSupportSubMixRecording:" + ret + " PackSha256:" + PackSha256);
return ret;
}
// …
} However, I did not find from where it is called. According to your stacktrace, it's from In any case, the obvious first attempt is to make the context initialized. This is what I did here. However, it was not suffient. So I don't know what to do. |
try {
AudioAttributes attributes3 = attributes2;
try {
int initResult = native_setup(new WeakReference(this), this.mAudioAttributes, sampleRate, this.mChannelMask, this.mChannelIndexMask, this.mAudioFormat, this.mNativeBufferSizeInBytes, session, attributionSourceState.getParcel(), 0L, maxSharedAudioHistoryMs);
if (initResult != 0) {
loge("Error code " + initResult + " when initializing native AudioRecord object.");
if (attributionSourceState != null) {
attributionSourceState.close();
return;
}
return;
}
if (attributionSourceState != null) {
attributionSourceState.close();
}
this.mSampleRate = sampleRate[0];
this.mSessionId = session[0];
IVivoAudioRecord iVivoAudioRecord = this.mVivoAudioRecord;
if (iVivoAudioRecord == null) {
i = 1;
} else {
String result = iVivoAudioRecord.isSupportSubMixRecording(); // <--- here
if (!"true".equals(result) || attributes3.getCapturePreset() != 8) {
i = 1;
} else {
i = 1;
this.mIsLiveApp = true;
this.mVivoAudioRecord.notifyGamecube(1);
this.mVivoAudioRecord.showNotificationStatus(true);
Log.d(TAG, "notifyGamecube start by " + this);
}
}
this.mState = i;
} catch (Throwable th2) {
th = th2;
if (attributionSourceState != null) {
try {
attributionSourceState.close();
} catch (Throwable th3) {
th.addSuppressed(th3);
}
}
throw th;
}
} catch (Throwable th4) {
th = th4;
} But looking at The query request was sent to system_server, which should be in |
I don't have this code in the |
I'm using jadx https://github.com/skylot/jadx |
Oh, I used jadx too, but an old version (v1.2.0). With the latest (v1.4.6), I get the same source as you 🎉 |
IVivoAudioRecord iVivoAudioRecord = this.mVivoAudioRecord;
if (iVivoAudioRecord == null) {
i = 1;
} else {
String result = iVivoAudioRecord.isSupportSubMixRecording(); Maybe we can force |
Let's try. @A-viral-dev please replace this binary in the v2.0 release, and run with
diffdiff --git a/server/src/main/java/com/genymobile/scrcpy/AudioCapture.java b/server/src/main/java/com/genymobile/scrcpy/AudioCapture.java
index 9228e3d71..ce0e09bde 100644
--- a/server/src/main/java/com/genymobile/scrcpy/AudioCapture.java
+++ b/server/src/main/java/com/genymobile/scrcpy/AudioCapture.java
@@ -55,7 +55,9 @@ public final class AudioCapture {
int minBufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE, CHANNEL_CONFIG, FORMAT);
// This buffer size does not impact latency
builder.setBufferSizeInBytes(8 * minBufferSize);
- return builder.build();
+ AudioRecord record = builder.build();
+ Workarounds.resetVivoAudioRecord(record);
+ return record;
}
private static void startWorkaroundAndroid11() {
diff --git a/server/src/main/java/com/genymobile/scrcpy/Workarounds.java b/server/src/main/java/com/genymobile/scrcpy/Workarounds.java
index 64cc12723..f4884c615 100644
--- a/server/src/main/java/com/genymobile/scrcpy/Workarounds.java
+++ b/server/src/main/java/com/genymobile/scrcpy/Workarounds.java
@@ -4,6 +4,7 @@ import android.annotation.SuppressLint;
import android.app.Application;
import android.content.ContextWrapper;
import android.content.pm.ApplicationInfo;
+import android.media.AudioRecord;
import android.os.Looper;
import java.lang.reflect.Constructor;
@@ -74,4 +75,15 @@ public final class Workarounds {
Ln.d("Could not fill app info: " + throwable.getMessage());
}
}
+
+ public static void resetVivoAudioRecord(AudioRecord record) {
+ try {
+ Field vivoAudioRecordField = AudioRecord.class.getDeclaredField("mVivoAudioRecord");
+ vivoAudioRecordField.setAccessible(true);
+ vivoAudioRecordField.set(record, null);
+ } catch (Throwable throwable) {
+ // this is a workaround, so failing is not an error
+ Ln.d("Could not reset vivo audio record: " + throwable.getMessage());
+ }
+ }
} |
|
Still no audio |
The call is in the constructor, must get the instance before the call, doesn't seem to be possible. Full decompiled source: https://gist.github.com/yume-chan/0e061cc819865d78d717b226513c27e9 Maybe invoke the |
Oh, it was so far below that I thought it was in another method. 🤦 |
Please try this: scrcpy-server.zip diffdiff --git a/server/build.gradle b/server/build.gradle
index ce234d10..644045af 100644
--- a/server/build.gradle
+++ b/server/build.gradle
@@ -20,6 +20,9 @@ android {
}
dependencies {
+ // https://mvnrepository.com/artifact/org.jooq/joor
+ implementation 'org.jooq:joor:0.9.14'
+
testImplementation 'junit:junit:4.13.2'
}
diff --git a/server/src/main/java/com/genymobile/scrcpy/AudioCapture.java b/server/src/main/java/com/genymobile/scrcpy/AudioCapture.java
index 6bb3ce23..eb65dedf 100644
--- a/server/src/main/java/com/genymobile/scrcpy/AudioCapture.java
+++ b/server/src/main/java/com/genymobile/scrcpy/AudioCapture.java
@@ -4,16 +4,23 @@ import com.genymobile.scrcpy.wrappers.ServiceManager;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
+import android.content.AttributionSource;
import android.content.ComponentName;
import android.content.Intent;
+import android.media.AudioAttributes;
import android.media.AudioFormat;
+import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTimestamp;
import android.media.MediaCodec;
import android.media.MediaRecorder;
import android.os.Build;
+import android.os.Looper;
import android.os.SystemClock;
+import org.joor.Reflect;
+
+import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
public final class AudioCapture {
@@ -34,28 +41,65 @@ public final class AudioCapture {
return SAMPLE_RATE * CHANNELS * BYTES_PER_SAMPLE * millis / 1000;
}
- private static AudioFormat createAudioFormat() {
- AudioFormat.Builder builder = new AudioFormat.Builder();
- builder.setEncoding(FORMAT);
- builder.setSampleRate(SAMPLE_RATE);
- builder.setChannelMask(CHANNEL_CONFIG);
- return builder.build();
- }
-
@TargetApi(Build.VERSION_CODES.M)
- @SuppressLint({"WrongConstant", "MissingPermission"})
+ @SuppressLint({ "WrongConstant", "MissingPermission" })
private static AudioRecord createAudioRecord() {
- AudioRecord.Builder builder = new AudioRecord.Builder();
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
- // On older APIs, Workarounds.fillAppInfo() must be called beforehand
- builder.setContext(FakeContext.get());
+ try {
+ Reflect audioRecord = Reflect.onClass(AudioRecord.class).create(0L);
+
+ audioRecord.set("mRecordingState", AudioRecord.RECORDSTATE_STOPPED);
+
+ Looper looper = Looper.myLooper();
+ if (looper == null) {
+ looper = Looper.getMainLooper();
+ }
+ audioRecord.set("mInitializationLooper", looper);
+
+ audioRecord.set("mIsSubmixFullVolume", true);
+
+ AudioAttributes.Builder audioAttributesBuilder = new AudioAttributes.Builder();
+ Reflect.on(audioAttributesBuilder).call("setInternalCapturePreset",
+ MediaRecorder.AudioSource.REMOTE_SUBMIX);
+ AudioAttributes audioAttributes = audioAttributesBuilder.build();
+ audioRecord.set("mAudioAttributes", audioAttributes);
+
+ audioRecord.call("audioParamCheck", MediaRecorder.AudioSource.REMOTE_SUBMIX, SAMPLE_RATE, FORMAT);
+
+ audioRecord.set("mChannelMask", CHANNEL_CONFIG);
+ audioRecord.set("mChannelCount", CHANNELS);
+
+ int minBufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE, CHANNEL_CONFIG, FORMAT);
+ // This buffer size does not impact latency
+ audioRecord.call("audioBuffSizeCheck", 8 * minBufferSize);
+
+ int[] sampleRate = new int[] { 0 };
+ int[] session = new int[] { AudioManager.AUDIO_SESSION_ID_GENERATE };
+
+ AttributionSource attributionSource = FakeContext.get().getAttributionSource();
+ Reflect attributionSourceState = Reflect.on(attributionSource).call("asScopedParcelState");
+ try (AutoCloseable closeable = attributionSourceState.as(AutoCloseable.class)) {
+ int initResult = audioRecord
+ .call("native_setup", (Object) new WeakReference<AudioRecord>(audioRecord.get()),
+ (Object) audioAttributes, sampleRate, CHANNEL_CONFIG, 0, FORMAT,
+ audioRecord.get("mNativeBufferSizeInBytes"), session,
+ attributionSourceState.call("getParcel").get(), 0L, 0)
+ .get();
+ if (initResult != AudioRecord.SUCCESS) {
+ Ln.e("Error code " + initResult + " when initializing native AudioRecord object.");
+ return null;
+ }
+ }
+
+ audioRecord.set("mSampleRate", sampleRate[0]);
+ audioRecord.set("mSessionId", session[0]);
+
+ audioRecord.set("mState", AudioRecord.STATE_INITIALIZED);
+
+ return audioRecord.get();
+ } catch (Throwable e) {
+ Ln.e("createAudioRecord", e);
+ return null;
}
- builder.setAudioSource(MediaRecorder.AudioSource.REMOTE_SUBMIX);
- builder.setAudioFormat(createAudioFormat());
- int minBufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE, CHANNEL_CONFIG, FORMAT);
- // This buffer size does not impact latency
- builder.setBufferSizeInBytes(8 * minBufferSize);
- return builder.build();
}
private static void startWorkaroundAndroid11() { |
D:\Download\scrcpy-win64-v2.0>scrcpy.exe met other issue exception in my vivo phone |
That's with a newer build from |
great sound start to work
But
|
Nice. Now let @rom1v decide whether to use this as a fallback path.
Yes this is expected. Remote submix is like a sound output (like a earphone), so all audio outputs are redirected to it. |
@yume-chan Great 👍 However, I'm a bit worried that it is a very specific/fragile code in the long run. On Android 11 it fails (because missing AttributeSource, but that's probably easy to fix). On Android 13 (non-vivo), it currently works though. |
I think it should be used as a fallback, if the normal path fails, try this one instead. It works on vanilla Android 12/13, but if another vendor also added some code to the constructor (that works with Scrcpy now), then the code will break them instead. So no need to support Android 11, unless another vivo user with Android 11 reported an issue. Also little effort is required to maintain it, if it stopped working, I don't care, we can always declare the device as can't be supported. |
OK, maybe. The code could may be moved in a new method Please tell me if you want to work on a proper PR to implement it. Otherwise, I could do it later. |
I can create a PR this weekend. |
It worked for me |
还是不行啊大佬 E:\touping\scrcpy-win64-v2.0\scrcpy-server: 1 file pushed, 0 skipped. 59.5 MB/s (62140 bytes in 0.001s) 有解决办法吗 |
Some devices (Vivo phones) fail to create an AudioRecord from an AudioRecord.Builder (which throw a NullPointerException). In that case, create an AudioRecord instance directly by reflection. The AOSP version of AudioRecord constructor code can be found at: - Android 11 (R): <https://cs.android.com/android/platform/superproject/+/android-11.0.0_r1:frameworks/base/media/java/android/media/AudioRecord.java;l=335;drc=64ed2ec38a511bbbd048985fe413268335e072f8> - Android 12 (S): <https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/media/java/android/media/AudioRecord.java;l=388;drc=2eebf929650e0d320a21f0d13677a27d7ab278e9> - Android 13 (T, functionally identical to Android 12): <https://cs.android.com/android/platform/superproject/+/android-13.0.0_r1:frameworks/base/media/java/android/media/AudioRecord.java;l=382;drc=ed242da52f975a1dd18671afb346b18853d729f2> - Android 14 (U): Not released, but expected to change PR #3862 <#3862> Fixes #3805 <#3805> Signed-off-by: Romain Vimont <[email protected]>
Some devices (Vivo phones) fail to create an AudioRecord from an AudioRecord.Builder (which throw a NullPointerException). In that case, create an AudioRecord instance directly by reflection. The AOSP version of AudioRecord constructor code can be found at: - Android 11 (R): <https://cs.android.com/android/platform/superproject/+/android-11.0.0_r1:frameworks/base/media/java/android/media/AudioRecord.java;l=335;drc=64ed2ec38a511bbbd048985fe413268335e072f8> - Android 12 (S): <https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/media/java/android/media/AudioRecord.java;l=388;drc=2eebf929650e0d320a21f0d13677a27d7ab278e9> - Android 13 (T, functionally identical to Android 12): <https://cs.android.com/android/platform/superproject/+/android-13.0.0_r1:frameworks/base/media/java/android/media/AudioRecord.java;l=382;drc=ed242da52f975a1dd18671afb346b18853d729f2> - Android 14 (U): Not released, but expected to change PR #3862 <#3862> Fixes #3805 <#3805> Signed-off-by: Romain Vimont <[email protected]>
vivo x80 audio no work |
@631jike Please test this version: #3862 (comment) |
(To Vivo device users) The Vivo issue with audio is fixed on However, I'm afraid that a work-in-progress fix (#4015) for another device might break the fix for Vivo phones, so I need you to test the following version to make sure scrcpy 2.1 will still work on Vivo phones. It basically contains the WIP fix branch and the following diff: diffdiff --git a/server/src/main/java/com/genymobile/scrcpy/AudioCapture.java b/server/src/main/java/com/genymobile/scrcpy/AudioCapture.java
index 7b20cce4f..2e9f7d058 100644
--- a/server/src/main/java/com/genymobile/scrcpy/AudioCapture.java
+++ b/server/src/main/java/com/genymobile/scrcpy/AudioCapture.java
@@ -105,7 +105,9 @@ public final class AudioCapture {
private void startRecording() {
try {
recorder = createAudioRecord(audioSource);
+ Ln.i("==== native AudioRecord worked");
} catch (NullPointerException e) {
+ Ln.e("==== native AudioRecord failed", e);
// Creating an AudioRecord using an AudioRecord.Builder does not work on Vivo phones:
// - <https://github.com/Genymobile/scrcpy/issues/3805>
// - <https://github.com/Genymobile/scrcpy/pull/3862> Here is a binary:
Please run it with your Vivo phone and post the whole console output. |
|
Audio did not work on Honor devices. Two workarounds are necessary: - a system context must be set as a base context of FakeContext (so that a PackageManager is available), - the same workaround as for Meizu phones must also be applied (so that ActivityThread.currentApplication() return a valid instance). These workarounds must not be applied for all devices, they cause other issues. Fixes #4015 <#4015> Refs #3085 <#3805> Co-authored-by: Simon Chan <[email protected]>
@Helaer Thank you very much, that helped a lot. Could you please try this new version: #4015 (comment) and confirm that audio still works on Vivo phones? |
@rom1v It works fine. C:\Users\mr.song\Downloads\scrcpy-win64-issue4015\scrcpy-win64-v2.0-93-g86e5c90ed>scrcpy -Vdebug |
Audio did not work on Honor devices. To make it work, a system context must be set as a base context of FakeContext (so that a PackageManager is available), and a current ActivityThread must be initialized. These workarounds must not be applied for all devices, because they might cause other issues. Fixes #4015 <#4015> Refs #3085 <#3805> Co-authored-by: Simon Chan <[email protected]>
Audio did not work on Honor devices. To make it work, a system context must be set as a base context of FakeContext (so that a PackageManager is available), and a current ActivityThread must be initialized. These workarounds must not be applied for all devices, because they might cause other issues. Fixes #4015 <#4015> Refs #3085 <#3805> Co-authored-by: Simon Chan <[email protected]>
Audio did not work on Honor devices. To make it work, a system context must be set as a base context of FakeContext (so that a PackageManager is available), and a current Application must be initialized. These workarounds must not be applied for all devices, because they might cause other issues. Fixes #4015 <#4015> Refs #3085 <#3805> Co-authored-by: Simon Chan <[email protected]>
Audio did not work on Honor devices. To make it work, a system context must be set as a base context of FakeContext (so that a PackageManager is available), and a current Application must be initialized. These workarounds must not be applied for all devices, because they might cause other issues. Fixes #4015 <#4015> Refs #3085 <#3805> Co-authored-by: Simon Chan <[email protected]>
@Helaer Unfortunately, the version above did not work for Honor devices, so here is a new one which is confirmed to work on Honor devices: #4015 (comment) Could you please confirm that it also works for Vivo devices? |
@rom1v It works fine. C:\Users\mr.song\Downloads\scrcpy-win64-honor_6\scrcpy-win64-v2.0-94-g9f9b852ae>scrcpy -Vdebug |
Audio did not work on Honor devices. To make it work, a system context must be set as a base context of FakeContext (so that a PackageManager is available), and a current Application and ActivityThread must be set. These workarounds must not be applied for all devices, because they might cause other issues. Fixes #4015 <#4015> Refs #3085 <#3805> Co-authored-by: Simon Chan <[email protected]>
FakeContext used ActivityThread.getSystemContext() as base context only in some cases, because it caused problems on some devices: - warnings on Xiaomi devices [1], which are now fixed by b8c5853 - issues related to Looper [2], which are solved by just calling Looper.prepare*() Therefore, we can now always assign a base context, which simplifies and helps to solve camera issues on some devices (#4392). [1] <#4015 (comment)> [2] <#3805 (comment)> Fixes #4392 <#4392>
Workarounds were disabled by default, and enabled only on some device or in specific conditions. But it seems they are needed for more and more devices, so enable them by default. They could be disabled for specific devices if necessary. --- Cases where workarounds caused issues: - <#940>: To be tested - <#3805 (comment)>: To be tested - <#4015 (comment)>: This is not a problem anymore since commit b8c5853.
@Helaer In reference to #3805 (comment), could you please test #5154? |
@rom1v The audio works fine on my vivo device. |
Oh, great 🎉 Thank you for the test 👍 |
Workarounds were disabled by default, and enabled only on some device or in specific conditions. But it seems they are needed for more and more devices, so enable them by default. They could be disabled for specific devices if necessary in the future. In the past, these workarounds caused a (harmless) exception to be printed on some Xiaomi devices [1]. But this is not a problem anymore since commit b8c5853. They also caused problems for audio on Vivo devices [2], but it seems this is not the case anymore [3]. They might also impact an old Nvidia Shield [4], but hopefully this is fixed now. [1]: <#4015 (comment)> [2]: <#3805 (comment)> [3]: <#3805 (comment)> [4]: <#940> PR #5154 <#5154>
Workarounds were disabled by default, and only enabled for some devices or under specific conditions. But it seems they are needed for more and more devices, so enable them by default. They could be disabled for specific devices if necessary in the future. In the past, these workarounds caused a (harmless) exception to be printed on some Xiaomi devices [1]. But this is not a problem anymore since commit b8c5853. They also caused problems for audio on Vivo devices [2], but it seems this is not the case anymore [3]. They might also impact an old Nvidia Shield [4], but hopefully this is fixed now. [1]: <#4015 (comment)> [2]: <#3805 (comment)> [3]: <#3805 (comment)> [4]: <#940> PR #5154 <#5154>
Workarounds were disabled by default, and only enabled for some devices or under specific conditions. But it seems they are needed for more and more devices, so enable them by default. They could be disabled for specific devices if necessary in the future. In the past, these workarounds caused a (harmless) exception to be printed on some Xiaomi devices [1]. But this is not a problem anymore since commit b8c5853. They also caused problems for audio on Vivo devices [2], but it seems this is not the case anymore [3]. They might also impact an old Nvidia Shield [4], but hopefully this is fixed now. [1]: <Genymobile#4015 (comment)> [2]: <Genymobile#3805 (comment)> [3]: <Genymobile#3805 (comment)> [4]: <Genymobile#940> PR Genymobile#5154 <Genymobile#5154>
Workarounds were disabled by default, and only enabled for some devices or under specific conditions. But it seems they are needed for more and more devices, so enable them by default. They could be disabled for specific devices if necessary in the future. In the past, these workarounds caused a (harmless) exception to be printed on some Xiaomi devices [1]. But this is not a problem anymore since commit b8c5853. They also caused problems for audio on Vivo devices [2], but it seems this is not the case anymore [3]. They might also impact an old Nvidia Shield [4], but hopefully this is fixed now. [1]: <Genymobile#4015 (comment)> [2]: <Genymobile#3805 (comment)> [3]: <Genymobile#3805 (comment)> [4]: <Genymobile#940> PR Genymobile#5154 <Genymobile#5154>
The text was updated successfully, but these errors were encountered: