Skip to content
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

Closed
A-viral-dev opened this issue Mar 13, 2023 · 38 comments
Closed

Audio not working on Vivo(Iqoo) Android 13 device #3805

A-viral-dev opened this issue Mar 13, 2023 · 38 comments

Comments

@A-viral-dev
Copy link

A-viral-dev commented Mar 13, 2023

scrcpy 2.0 <https://github.com/Genymobile/scrcpy>
/usr/local/share/scrcpy/scrcpy-server: 1 file pushed. 4.8 MB/s (52867 bytes in 0.010s)
[server] INFO: Device: vivo I2202 (Android 13)
INFO: Renderer: opengl
INFO: OpenGL version: 3.1 Mesa 21.2.6
INFO: Trilinear filtering enabled
INFO: Initial texture: 1080x2400
WARN: Demuxer 'audio': stream explicitly disabled by the device
[server] ERROR: Exception on thread Thread[Thread-4,5,main]
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Context.getOpPackageName()' on a null object reference
        at android.media.VivoAudioRecordImpl.isSupportSubMixRecording(VivoAudioRecordImpl.java:133)
        at android.media.AudioRecord.<init>(AudioRecord.java:493)
        at android.media.AudioRecord.<init>(Unknown Source:0)
        at android.media.AudioRecord$Builder.build(AudioRecord.java:999)
        at com.genymobile.scrcpy.AudioCapture.createAudioRecord(AudioCapture.java:58)
        at com.genymobile.scrcpy.AudioCapture.start(AudioCapture.java:90)
        at com.genymobile.scrcpy.AudioEncoder.encode(AudioEncoder.java:183)
        at com.genymobile.scrcpy.AudioEncoder.lambda$start$0$com-genymobile-scrcpy-AudioEncoder(AudioEncoder.java:120)
        at com.genymobile.scrcpy.AudioEncoder$$ExternalSyntheticLambda0.run(Unknown Source:2)
        at java.lang.Thread.run(Thread.java:1012)
@rom1v
Copy link
Collaborator

rom1v commented Mar 13, 2023

Yes, unfortunately it doesn't work on some vivo devices: #3757 (comment) #3791 (comment)

@rom1v
Copy link
Collaborator

rom1v commented Mar 16, 2023

Following the discussion from #3791 (because here is the right place for this issue).

I asked to pull the following .jar:

adb pull /system/framework/framework.jar
adb pull /system/framework/vivo-framework.jar
adb pull /system/framework/vivo-media.jar

Here are the links you provided:

And the list of available jarfiles in /system/framework:

adb shell ls /system/framework
QPerformance.jar
QXPerformance.jar
UxPerformance.jar
WfdCommon.jar
abx.jar
am.jar
android.hardware.wifi.supplicant-V1.0-java.jar
android.hardware.wifi.supplicant-V1.1-java.jar
android.hardware.wifi.supplicant-V1.2-java.jar
android.hardware.wifi.supplicant-V1.3-java.jar
android.hidl.base-V1.0-java.jar
android.hidl.manager-V1.0-java.jar
android.test.base.jar
android.test.mock.jar
android.test.runner.jar
appwidget.jar
arm
arm64
bmgr.jar
boot-QPerformance.vdex
boot-UxPerformance.vdex
boot-WfdCommon.vdex
boot-core-icu4j.vdex
boot-ext.vdex
boot-framework-adapter.vdex
boot-framework-graphics.vdex
boot-framework.vdex
boot-ims-common.vdex
boot-qcom.fmradio.vdex
boot-soc-framework.vdex
boot-tcmiface.vdex
boot-telephony-common.vdex
boot-telephony-ext.vdex
boot-vivo-framework.vdex
boot-vivo-media.vdex
boot-vivo-vgcclient.vdex
boot-voip-common.vdex
bu.jar
com.android.future.usb.accessory.jar
com.android.location.provider.jar
com.android.media.remotedisplay.jar
com.android.mediadrm.signer.jar
com.android.wm.shell.jar
com.novatek.framework.jar
content.jar
ext.jar
framework-adapter.jar
framework-graphics.jar
framework-res.apk
framework.jar
hid.jar
ims-common.jar
incident-helper-cmd.jar
javax.obex.jar
locksettings.jar
monkey.jar
oat
org.apache.http.legacy.jar
org.apache.http.legacy.jar.prof
qcom.fmradio.jar
requestsync.jar
services-adapter.jar
services.jar
services.jar.bprof
services.jar.prof
sm.jar
soc-framework.jar
soc-services.jar
svc.jar
tcmclient.jar
tcmiface.jar
telecom.jar
telephony-common.jar
telephony-ext.jar
uiautomator.jar
uinput.jar
vendor.qti.hardware.radio.qtiradio-V1-java.jar
vivo-framework.jar
vivo-media.jar
vivo-priv-telephony-common.jar
vivo-res.apk
vivo-services.jar
vivo-telephony-common-platform-ex.jar
vivo-telephony-common.jar
vivo-telephony-ext.jar
vivo-vgcclient.jar
voip-common.jar
vr.jar
wapicertstore.jar
wmshellapp.apk

The class VivoAudioRecordImpl is found in vivo-framework.jar. Here are the relevant parts:

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 android.media.AudioRecord. This class is available in your framework.jar, but does not call isSupportSubMixRecording().

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.

@yume-chan
Copy link
Contributor

isSupportSubMixRecording is being called from AudioRecord's constructor,

        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 isSupportSubMixRecording, maybe vivo only allows some apps to capture remote submix, Even if we have all methods on context, it might still not work.

The query request was sent to system_server, which should be in services.jar or vivo-services.jar

@rom1v
Copy link
Collaborator

rom1v commented Mar 16, 2023

isSupportSubMixRecording is being called from AudioRecord's constructor,

I don't have this code in the AudioRecord.java from framework.jar. How did you decompile?

@yume-chan
Copy link
Contributor

I'm using jadx https://github.com/skylot/jadx

@rom1v
Copy link
Collaborator

rom1v commented Mar 16, 2023

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 🎉

@rom1v
Copy link
Collaborator

rom1v commented Mar 16, 2023

                IVivoAudioRecord iVivoAudioRecord = this.mVivoAudioRecord;
                if (iVivoAudioRecord == null) {
                    i = 1;
                } else {
                    String result = iVivoAudioRecord.isSupportSubMixRecording();

Maybe we can force mVivoAudioRecord to be null 🙈

@rom1v
Copy link
Collaborator

rom1v commented Mar 16, 2023

Let's try. @A-viral-dev please replace this binary in the v2.0 release, and run with scrcpy -Vdebug:

  • scrcpy-server SHA-256: 682d39c1cbe24ae1279378b1c13b4d28106407ae5fa26db72613fdfeaec7f5e8
diff
diff --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());
+        }
+    }
 }

@A-viral-dev
Copy link
Author

A-viral-dev commented Mar 16, 2023

`aviralshivpuri@penguin:~$ scrcpy -Vdebug
scrcpy 2.0 <https://github.com/Genymobile/scrcpy>
DEBUG: ADB device found:
DEBUG:     --> (tcpip)  192.168.29.216:5555             device  I2202
DEBUG: Device serial: 192.168.29.216:5555
DEBUG: Using server: /usr/local/share/scrcpy/scrcpy-server
/usr/local/share/scrcpy/scrcpy-server: 1 file pushed. 1.4 MB/s (129307 bytes in 0.086s)
[server] INFO: Device: vivo I2202 (Android 13)
DEBUG: Server connected
DEBUG: Starting controller thread
DEBUG: Starting receiver thread
INFO: Renderer: opengl
INFO: OpenGL version: 3.1 Mesa 21.2.6
INFO: Trilinear filtering enabled
DEBUG: Using icon: /usr/local/share/icons/hicolor/256x256/apps/scrcpy.png
DEBUG: Demuxer 'video': starting thread
DEBUG: Demuxer 'audio': starting thread
[server] DEBUG: Using encoder: 'OMX.qcom.video.encoder.avc'
INFO: Initial texture: 1080x2400
[server] DEBUG: Using audio encoder: 'c2.android.opus.encoder'
WARN: Demuxer 'audio': stream explicitly disabled by the device
[server] DEBUG: Audio encoder stopped
[server] ERROR: Exception on thread Thread[Thread-4,5,main]
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Context.getOpPackageName()' on a null object reference
        at android.media.VivoAudioRecordImpl.isSupportSubMixRecording(VivoAudioRecordImpl.java:133)
        at android.media.AudioRecord.<init>(AudioRecord.java:493)
        at android.media.AudioRecord.<init>(Unknown Source:0)
        at android.media.AudioRecord$Builder.build(AudioRecord.java:999)
        at com.genymobile.scrcpy.AudioCapture.createAudioRecord(AudioCapture.java:58)
        at com.genymobile.scrcpy.AudioCapture.start(AudioCapture.java:92)
        at com.genymobile.scrcpy.AudioEncoder.encode(AudioEncoder.java:183)
        at com.genymobile.scrcpy.AudioEncoder.lambda$start$0$com-genymobile-scrcpy-AudioEncoder(AudioEncoder.java:120)
        at com.genymobile.scrcpy.AudioEncoder$$ExternalSyntheticLambda0.run(Unknown Source:2)
        at java.lang.Thread.run(Thread.java:1012)`

@A-viral-dev
Copy link
Author

Still no audio

@yume-chan
Copy link
Contributor

yume-chan commented Mar 16, 2023

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 AudioRecord(long nativeRecordInJavaObj) constructor by reflection (with 0) and do all initialization (native_setup) in Scrcpy?

@rom1v
Copy link
Collaborator

rom1v commented Mar 16, 2023

The call is in the constructor

Oh, it was so far below that I thought it was in another method. 🤦

@yume-chan
Copy link
Contributor

yume-chan commented Mar 17, 2023

Please try this: scrcpy-server.zip

diff
diff --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() {

@Ryan2009
Copy link

D:\Download\scrcpy-win64-v2.0>scrcpy.exe
scrcpy 2.0 https://github.com/Genymobile/scrcpy
D:\Download\scrcpy-win64-v2.0\scrcpy-server: 1 file pushed, 0 skipped. 68.7 MB/s (62140 bytes in 0.001s)
[server] INFO: Device: vivo V2118A (Android 12)
INFO: Renderer: direct3d
INFO: Initial texture: 1080x2408
[server] ERROR: Audio capture error
java.io.IOException: Could not read audio: 0
at com.genymobile.scrcpy.AudioEncoder.inputThread(AudioEncoder.java:96)
at com.genymobile.scrcpy.AudioEncoder.lambda$encode$1$com-genymobile-scrcpy-AudioEncoder(AudioEncoder.java:189)
at com.genymobile.scrcpy.AudioEncoder$$ExternalSyntheticLambda1.run(Unknown Source:6)
at java.lang.Thread.run(Thread.java:922)

met other issue exception in my vivo phone

@rom1v
Copy link
Collaborator

rom1v commented Mar 19, 2023

That's with a newer build from dev branch. Ibdon't have any solution fornvivo phones.

@A-viral-dev
Copy link
Author

A-viral-dev commented Mar 19, 2023

great sound start to work

scrcpy 2.0 <https://github.com/Genymobile/scrcpy>
/usr/local/share/scrcpy/scrcpy-server: 1 file pushed, 0 skipped. 117.7 MB/s (62140 bytes in 0.001s)
[server] INFO: Device: vivo I2202 (Android 13)
INFO: Renderer: opengl
INFO: OpenGL version: 3.1 Mesa 21.2.6
INFO: Trilinear filtering enabled
INFO: Initial texture: 1080x2400

But

  1. The captured video and the device performance become very slow and laggy when scrcpy is running. Its my home wifi, its working fine on usb and other network.
  2. Another issue is that the sound from the device is only played on the computer and not on both devices. Maybe WAD.

@yume-chan
Copy link
Contributor

great sound start to work

Nice. Now let @rom1v decide whether to use this as a fallback path.

the sound from the device is only played on the computer and not on both devices.

Yes this is expected. Remote submix is like a sound output (like a earphone), so all audio outputs are redirected to it.

@rom1v
Copy link
Collaborator

rom1v commented Mar 19, 2023

@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.

@yume-chan
Copy link
Contributor

yume-chan commented Mar 19, 2023

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.

@rom1v
Copy link
Collaborator

rom1v commented Mar 19, 2023

OK, maybe. The code could may be moved in a new method Workarounds.createAudioRecord(), and with native reflection (not to add a joor dependency). I think it may be adapted for Android 11 too (just removing the AttributeSource stuff).

Please tell me if you want to work on a proper PR to implement it. Otherwise, I could do it later.

@yume-chan
Copy link
Contributor

I can create a PR this weekend.

@mziyadrahmani
Copy link

Please try this (only for testing whether vivo has an allowlist for capturing remote submix): scrcpy-server.zip

diff

It worked for me

@zwjgi
Copy link

zwjgi commented Apr 10, 2023

Please try this: scrcpy-server.zip

diff

@yume-chan

还是不行啊大佬

E:\touping\scrcpy-win64-v2.0\scrcpy-server: 1 file pushed, 0 skipped. 59.5 MB/s (62140 bytes in 0.001s)
[server] INFO: Device: vivo V2055A (Android 11)
[server] ERROR: createAudioRecord
java.lang.NoClassDefFoundError: Failed resolution of: Landroid/content/AttributionSource$Builder;
at com.genymobile.scrcpy.FakeContext.getAttributionSource(FakeContext.java:37)
at com.genymobile.scrcpy.AudioCapture.createAudioRecord(AudioCapture.java:78)
at com.genymobile.scrcpy.AudioCapture.start(AudioCapture.java:134)
at com.genymobile.scrcpy.AudioEncoder.encode(AudioEncoder.java:183)
at com.genymobile.scrcpy.AudioEncoder.lambda$start$0$com-genymobile-scrcpy-AudioEncoder(AudioEncoder.java:120)
at com.genymobile.scrcpy.AudioEncoder$$ExternalSyntheticLambda0.run(Unknown Source:2)
at java.lang.Thread.run(Thread.java:923)
Caused by: java.lang.ClassNotFoundException: android.content.AttributionSource$Builder
... 7 more
INFO: Renderer: direct3d
INFO: Initial texture: 1080x2400
WARN: Demuxer 'audio': stream explicitly disabled by the device
[server] ERROR: Exception on thread Thread[Thread-4,5,main]
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.media.AudioRecord.startRecording()' on a null object reference
at com.genymobile.scrcpy.AudioCapture.start(AudioCapture.java:135)
at com.genymobile.scrcpy.AudioEncoder.encode(AudioEncoder.java:183)
at com.genymobile.scrcpy.AudioEncoder.lambda$start$0$com-genymobile-scrcpy-AudioEncoder(AudioEncoder.java:120)
at com.genymobile.scrcpy.AudioEncoder$$ExternalSyntheticLambda0.run(Unknown Source:2)
at java.lang.Thread.run(Thread.java:923)

有解决办法吗

rom1v pushed a commit that referenced this issue May 24, 2023
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]>
rom1v pushed a commit that referenced this issue May 25, 2023
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]>
@631jike
Copy link

631jike commented Jun 10, 2023

vivo x80 audio no work

@rom1v
Copy link
Collaborator

rom1v commented Jun 10, 2023

@631jike Please test this version: #3862 (comment)

@rom1v
Copy link
Collaborator

rom1v commented Jun 18, 2023

(To Vivo device users)

The Vivo issue with audio is fixed on dev branch and it should work on the future scrcpy 2.1 release.

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:

diff
diff --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.

@Helaer
Copy link

Helaer commented Jun 18, 2023

@rom1v

C:\Users\mr.song\Downloads\scrcpy-win64-issue3805\scrcpy-win64-v2.0-95-g9bff8ccad>scrcpy -Vdebug
scrcpy 2.0 <https://github.com/Genymobile/scrcpy>
DEBUG: ADB device found:
DEBUG:     -->   (usb)       3468348101003D0            device  V2157A
DEBUG: Device serial: 3468348101003D0
DEBUG: Using server (portable): C:\Users\mr.song\Downloads\scrcpy-win64-issue3805\scrcpy-win64-v2.0-95-g9bff8ccad\scrcpy-server
C:\Users\mr.song\Downloads\scrcpy-win64-issue3805\scrcpy-w... file pushed, 0 skipped. 34.7 MB/s (57051 bytes in 0.002s)
[server] INFO: Device: [vivo] vivo V2157A (Android 13)
DEBUG: Server connected
DEBUG: Starting controller thread
DEBUG: Starting receiver thread
[server] DEBUG: Using video encoder: 'OMX.qcom.video.encoder.avc'
[server] DEBUG: Using audio encoder: 'c2.android.opus.encoder'
INFO: Renderer: direct3d
DEBUG: Trilinear filtering disable[server] DEBUG: Audio encoder stopped
d (not an OpenGL renderer
DEBUG: Using icon (portable): C:\Users\mr.song\Downloads\scrcpy-win64-issue3805\scrcpy-win64-v2.0-95-g9bff8ccad\icon.pngDEBUG: Demuxer 'video': starting thread
DEBUG: Demuxer 'audi[server] ERROR: Exception on thread Thread[audio-encoder,5,main]
o': starting thread
WARN: Demuxer 'java.lang.AssertionError: java.lang.reflect.InvocationTargetExceptiona
u       at com.genymobile.scrcpy.wrappers.ActivityThread.<clinit>(ActivityThread.java:17)d
i       at com.genymobile.scrcpy.wrappers.ActivityThread.getActivityThreadClass(ActivityThread.java:30)o
'       at com.genymobile.scrcpy.FakeContext.retrieveSystemContext(FakeContext.java:23):
        at com.genymobile.scrcpy.FakeContext.<init>(FakeContext.java:39)s
t       at com.genymobile.scrcpy.FakeContext.<clinit>(FakeContext.java:19)r
e       at com.genymobile.scrcpy.FakeContext.get(FakeContext.java:35)am exp
l       at com.genymobile.scrcpy.AudioCapture.createAudioRecord(AudioCapture.java:57)i
c       at com.genymobile.scrcpy.AudioCapture.startRecording(AudioCapture.java:107)i
t       at com.genymobile.scrcpy.AudioCapture.start(AudioCapture.java:128)l
y       at com.genymobile.scrcpy.AudioEncoder.encode(AudioEncoder.java:193)
d       at com.genymobile.scrcpy.AudioEncoder.lambda$start$0$com-genymobile-scrcpy-AudioEncoder(AudioEncoder.java:124)i
s       at com.genymobile.scrcpy.AudioEncoder$$ExternalSyntheticLambda0.run(Unknown Source:4)a
b       at java.lang.Thread.run(Thread.java:1015)l
eCaused by: java.lang.reflect.InvocationTargetExceptiond
        at java.lang.reflect.Constructor.newInstance0(Native Method)b
y       at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
t       at com.genymobile.scrcpy.wrappers.ActivityThread.<clinit>(ActivityThread.java:15)h
e       ... 12 more
deCaused by: java.lang.RuntimeException: Can't create handler inside thread Thread[audio-encoder,5,main] that has not called Looper.prepare()v
i       at android.os.Handler.<init>(Handler.java:227)c
e       at android.os.Handler.<init>(Handler.java:129)

I       at android.app.ActivityThread$H.<init>(ActivityThread.java:2262)N
F       at android.app.ActivityThread.<init>(ActivityThread.java:420)O
:       ... 15 more
Texture: 1080x2400

rom1v added a commit that referenced this issue Jun 18, 2023
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]>
@rom1v
Copy link
Collaborator

rom1v commented Jun 18, 2023

@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?

@Helaer
Copy link

Helaer commented Jun 19, 2023

@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
scrcpy 2.0 https://github.com/Genymobile/scrcpy
DEBUG: ADB device found:
DEBUG: --> (usb) 3468348101003D0 device V2157A
DEBUG: Device serial: 3468348101003D0
DEBUG: Using server (portable): C:\Users\mr.song\Downloads\scrcpy-win64-issue4015\scrcpy-win64-v2.0-93-g86e5c90ed\scrcpy-server
C:\Users\mr.song\Downloads\scrcpy-win64-issue4015\scrcpy-w... file pushed, 0 skipped. 37.2 MB/s (56923 bytes in 0.001s)
[server] INFO: Device: [vivo] vivo V2157A (Android 13)
[server] DEBUG: Using video encoder: 'OMX.qcom.video.encoder.avc'
[server] DEBUG: Using audio encoder: 'c2.android.opus.encoder'
DEBUG: Server connected
DEBUG: Starting controller thread
DEBUG: Starting receiver thread
INFO: Renderer: direct3d
DEBUG: Trilinear filtering disabled (not an OpenGL renderer
DEBUG: Using icon (portable): C:\Users\mr.song\Downloads\scrcpy-win64-issue4015\scrcpy-win64-v2.0-93-g86e5c90ed\icon.png
DEBUG: Demuxer 'video': starting thread
DEBUG: Demuxer 'audio': starting thread
INFO: Texture: 1080x2400
DEBUG: [Audio] Buffering threshold exceeded, skipping 240 samples
DEBUG: [Audio] Buffering threshold exceeded, skipping 960 samples
DEBUG: [Audio] Buffering threshold exceeded, skipping 480 samples
DEBUG: [Audio] Buffering threshold exceeded, skipping 960 samples
DEBUG: [Audio] Buffering threshold exceeded, skipping 960 samples
DEBUG: [Audio] Buffering threshold exceeded, skipping 960 samples
DEBUG: [Audio] Buffering threshold exceeded, skipping 480 samples

rom1v added a commit that referenced this issue Jun 19, 2023
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]>
rom1v added a commit that referenced this issue Jun 19, 2023
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]>
rom1v added a commit that referenced this issue Jun 19, 2023
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]>
rom1v added a commit that referenced this issue Jun 19, 2023
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]>
@rom1v
Copy link
Collaborator

rom1v commented Jun 19, 2023

@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?

@Helaer
Copy link

Helaer commented Jun 19, 2023

不幸的是,上面的版本不适用于荣耀设备,所以这里有一个确认适用于荣耀设备的新版本:#4015(评论)

您能否确认它也适用于Vivo设备?

@rom1v It works fine.

C:\Users\mr.song\Downloads\scrcpy-win64-honor_6\scrcpy-win64-v2.0-94-g9f9b852ae>scrcpy -Vdebug
scrcpy 2.0 https://github.com/Genymobile/scrcpy
DEBUG: ADB device found:
DEBUG: --> (usb) 3468348101003D0 device V2157A
DEBUG: Device serial: 3468348101003D0
DEBUG: Using server (portable): C:\Users\mr.song\Downloads\scrcpy-win64-honor_6\scrcpy-win64-v2.0-94-g9f9b852ae\scrcpy-server
C:\Users\mr.song\Downloads\scrcpy-win64-honor_6\scrcpy-win...file pushed, 0 skipped. 125.7 MB/s (56935 bytes in 0.000s)
[server] INFO: Device: [vivo] vivo V2157A (Android 13)
DEBUG: Server connected
DEBUG: Starting controller thread
DEBUG: Starting receiver thread
[server] DEBUG: Using audio encoder: 'c2.android.opus.encoder'
[server] DEBUG: Using video encoder: 'OMX.qcom.video.encoder.avc'
INFO: Renderer: direct3d
DEBUG: Trilinear filtering disabled (not an OpenGL renderer
DEBUG: Using icon (portable): C:\Users\mr.song\Downloads\scrcpy-win64-honor_6\scrcpy-win64-v2.0-94-g9f9b852ae\icon.png
DEBUG: Demuxer 'video': starting thread
DEBUG: Demuxer 'audio': starting thread
INFO: Texture: 1080x2400
DEBUG: [Audio] Buffering threshold exceeded, skipping 240 samples
DEBUG: [Audio] Buffering threshold exceeded, skipping 480 samples
DEBUG: [Audio] Buffer underflow, inserting silence: 87 samples
DEBUG: [Audio] Buffer underflow, inserting silence: 240 samples
DEBUG: [Audio] Buffer underflow, inserting silence: 240 samples

rom1v added a commit that referenced this issue Jun 19, 2023
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]>
@rom1v rom1v closed this as completed in cab3541 Jun 21, 2023
rom1v added a commit that referenced this issue Nov 3, 2023
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>
rom1v added a commit that referenced this issue Jul 30, 2024
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.
@rom1v
Copy link
Collaborator

rom1v commented Jul 30, 2024

@Helaer In reference to #3805 (comment), could you please test #5154?

@Helaer
Copy link

Helaer commented Jul 31, 2024

@Helaer In reference to #3805 (comment), could you please test #5154?

@rom1v The audio works fine on my vivo device.

@rom1v
Copy link
Collaborator

rom1v commented Jul 31, 2024

Oh, great 🎉 Thank you for the test 👍

rom1v added a commit that referenced this issue Jul 31, 2024
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>
rom1v added a commit that referenced this issue Aug 1, 2024
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>
FreedomBen pushed a commit to FreedomBen/scrcpy that referenced this issue Aug 2, 2024
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>
Gottox pushed a commit to Gottox/scrcpy that referenced this issue Sep 29, 2024
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants