1
0
mirror of git://f0xx.org/android_cast synced 2026-07-29 08:19:00 +03:00
This commit is contained in:
Anton Afanasyeu
2026-06-02 15:24:11 +02:00
parent 9f9d617730
commit 5e940690e9
35 changed files with 896 additions and 41 deletions

View File

@@ -45,7 +45,9 @@ public class CastSettings implements Serializable {
/** Future Speex (UI disabled until integrated). */
SPEEX,
/** Future Opus with in-band FEC (UI disabled until integrated). */
OPUS
OPUS,
/** Explicit AAC passthrough wrapper for debug comparisons. */
PASSTHROUGH_DEBUG
}
public enum AdjustmentPreset {

View File

@@ -604,6 +604,8 @@ public final class CastSettingsBinder {
return activity.getString(R.string.codec_speex);
case OPUS:
return activity.getString(R.string.codec_opus);
case PASSTHROUGH_DEBUG:
return activity.getString(R.string.codec_audio_passthrough_debug);
case AUTO:
default:
return activity.getString(R.string.codec_audio_auto);

View File

@@ -16,6 +16,7 @@ import com.foxx.androidcast.CastTuningEngine;
import com.foxx.androidcast.media.CodecNegotiator;
import com.foxx.androidcast.media.codec.CodecSessionRegistry;
import com.foxx.androidcast.media.codec.PassthroughCodecPolicy;
import com.foxx.androidcast.media.codec.jni.NativeCodecBridge;
import com.foxx.androidcast.network.control.NetworkStatsSnapshot;
import com.foxx.androidcast.network.diag.DiagPingMetrics;
import com.foxx.androidcast.receiver.StreamMetrics;
@@ -393,9 +394,17 @@ public final class CastDiagnosticsFormatter {
if (prefs != null && prefs.isAudioRequested()) {
CastSettings.AudioCodec ac = prefs.getAudioCodec();
if (ac == CastSettings.AudioCodec.OPUS) {
html.append(" · <font color='").append(COLOR_OK).append("'>Opus in-band FEC</font>");
if (NativeCodecBridge.isOpusAvailable()) {
html.append(" · <font color='").append(COLOR_OK).append("'>Opus in-band FEC</font>");
} else {
html.append(" · <font color='").append(COLOR_WARN).append("'>Opus fallback AAC</font>");
}
} else if (ac == CastSettings.AudioCodec.SPEEX) {
html.append(" · <font color='").append(COLOR_OK).append("'>Speex FEC</font>");
if (NativeCodecBridge.isSpeexAvailable()) {
html.append(" · <font color='").append(COLOR_OK).append("'>Speex FEC</font>");
} else {
html.append(" · <font color='").append(COLOR_WARN).append("'>Speex fallback AAC</font>");
}
} else {
html.append(" · AAC-LC (MediaCodec)");
}

View File

@@ -12,6 +12,7 @@ package com.foxx.androidcast.diagnostics;
* Digest: SHA256 1c6f25ad871cb71788ed6fd1817d4d59d22d5ef6638704c9483937a2a9c15977
**********************************************************************/
import com.foxx.androidcast.CastSettings;
import com.foxx.androidcast.media.codec.jni.NativeCodecBridge;
/** Human-readable names for diagnostics (no bare AUTO enum tokens). */
public final class CastDiagnosticsLabels {
@@ -137,9 +138,13 @@ public final class CastDiagnosticsLabels {
case AAC:
return "AAC-LC (forced)";
case SPEEX:
return "Speex (stub → AAC)";
return NativeCodecBridge.isSpeexAvailable()
? "Speex (native)" : "Speex (fallback AAC)";
case OPUS:
return "Opus + in-band FEC (stub → AAC)";
return NativeCodecBridge.isOpusAvailable()
? "Opus + in-band FEC (native)" : "Opus (fallback AAC)";
case PASSTHROUGH_DEBUG:
return "Passthrough AAC (debug)";
case AUTO:
default:
return "AUTO (AAC via MediaCodec)";

View File

@@ -248,10 +248,13 @@ public final class CodecPriorityCatalog {
public static CodecTier tierForAudioPreference(CastSettings.AudioCodec preference) {
if (preference == CastSettings.AudioCodec.SPEEX) {
return CodecTier.PASSTHROUGH;
return NativeCodecBridge.isSpeexAvailable() ? CodecTier.SOFTWARE : CodecTier.PASSTHROUGH;
}
if (preference == CastSettings.AudioCodec.OPUS) {
return CodecTier.SOFTWARE;
return NativeCodecBridge.isOpusAvailable() ? CodecTier.SOFTWARE : CodecTier.PASSTHROUGH;
}
if (preference == CastSettings.AudioCodec.PASSTHROUGH_DEBUG) {
return CodecTier.PASSTHROUGH;
}
return CodecTier.HARDWARE;
}

View File

@@ -84,6 +84,8 @@ public enum CodecPriorityId {
return OPUS;
case SPEEX:
return SPEEX;
case PASSTHROUGH_DEBUG:
return PCM;
case AUTO:
default:
return null;

View File

@@ -14,8 +14,11 @@ package com.foxx.androidcast.media.codec;
/** Implementation backing negotiated / configured audio. */
public enum AudioCodecBackend {
MEDIA_CODEC_AAC("MediaCodec AAC-LC"),
SPEEX_NATIVE("Speex native"),
OPUS_NATIVE("Opus native"),
SPEEX_STUB("Speex (stub → AAC passthrough)"),
OPUS_STUB("Opus (stub → AAC passthrough)"),
PASSTHROUGH_DEBUG("Passthrough AAC (debug)"),
DISABLED("off"),
UNKNOWN("?");

View File

@@ -21,7 +21,9 @@ public final class AudioCodecFactory {
public static AudioEncoderSink createEncoder(CastSettings.AudioCodec preference) {
CodecSessionRegistry.setAudioPreference(preference);
AudioCodecBackend backend = PassthroughCodecPolicy.audioBackendFor(preference, true);
if (backend == AudioCodecBackend.SPEEX_STUB || backend == AudioCodecBackend.OPUS_STUB) {
if (backend == AudioCodecBackend.SPEEX_STUB
|| backend == AudioCodecBackend.OPUS_STUB
|| backend == AudioCodecBackend.PASSTHROUGH_DEBUG) {
return new PassthroughAudioEncoderSink(new AudioEncoder(), preference);
}
return new AudioEncoder();

View File

@@ -31,6 +31,8 @@ final class PassthroughAudioEncoderSink implements AudioEncoderSink {
CodecSessionRegistry.setAudioBackend(AudioCodecBackend.OPUS_STUB);
} else if (preference == CastSettings.AudioCodec.SPEEX) {
CodecSessionRegistry.setAudioBackend(AudioCodecBackend.SPEEX_STUB);
} else if (preference == CastSettings.AudioCodec.PASSTHROUGH_DEBUG) {
CodecSessionRegistry.setAudioBackend(AudioCodecBackend.PASSTHROUGH_DEBUG);
}
delegate.prepare(callback);
}

View File

@@ -57,7 +57,12 @@ public final class PassthroughCodecPolicy {
}
switch (preference) {
case SPEEX:
return NativeCodecBridge.isSpeexAvailable()
? CastSettings.AudioCodec.SPEEX : CastSettings.AudioCodec.AUTO;
case OPUS:
return NativeCodecBridge.isOpusAvailable()
? CastSettings.AudioCodec.OPUS : CastSettings.AudioCodec.AUTO;
case PASSTHROUGH_DEBUG:
return CastSettings.AudioCodec.AUTO;
default:
return preference;
@@ -70,10 +75,15 @@ public final class PassthroughCodecPolicy {
return AudioCodecBackend.DISABLED;
}
if (preference == CastSettings.AudioCodec.SPEEX) {
return AudioCodecBackend.SPEEX_STUB;
return NativeCodecBridge.isSpeexAvailable()
? AudioCodecBackend.SPEEX_NATIVE : AudioCodecBackend.SPEEX_STUB;
}
if (preference == CastSettings.AudioCodec.OPUS) {
return AudioCodecBackend.OPUS_STUB;
return NativeCodecBridge.isOpusAvailable()
? AudioCodecBackend.OPUS_NATIVE : AudioCodecBackend.OPUS_STUB;
}
if (preference == CastSettings.AudioCodec.PASSTHROUGH_DEBUG) {
return AudioCodecBackend.PASSTHROUGH_DEBUG;
}
return AudioCodecBackend.MEDIA_CODEC_AAC;
}
@@ -150,6 +160,8 @@ public final class PassthroughCodecPolicy {
return "Opus";
case AAC:
return "AAC";
case PASSTHROUGH_DEBUG:
return "Passthrough (debug)";
case AUTO:
default:
return "AUTO (AAC)";

View File

@@ -13,6 +13,7 @@ package com.foxx.androidcast.network;
**********************************************************************/
import android.media.MediaFormat;
import com.foxx.androidcast.media.codec.jni.NativeCodecBridge;
import com.foxx.androidcast.sender.CodecCatalog;
import java.util.List;
@@ -28,6 +29,8 @@ public final class CastCodecFlags {
public static final int AUDIO_UNKNOWN = 0;
public static final int AUDIO_PCM = 1;
public static final int AUDIO_AAC = 2;
public static final int AUDIO_OPUS = 4;
public static final int AUDIO_SPEEX = 8;
private CastCodecFlags() {}
@@ -59,6 +62,13 @@ public final class CastCodecFlags {
}
public static int localAudioMask() {
return AUDIO_AAC | AUDIO_PCM;
int mask = AUDIO_AAC | AUDIO_PCM;
if (NativeCodecBridge.isOpusAvailable()) {
mask |= AUDIO_OPUS;
}
if (NativeCodecBridge.isSpeexAvailable()) {
mask |= AUDIO_SPEEX;
}
return mask;
}
}

View File

@@ -103,6 +103,7 @@ public final class CastProtocol {
dos.writeInt(settings.getCaptureMode().ordinal());
dos.writeInt(settings.getStreamProtection().ordinal());
dos.writeInt(settings.getReceiverDisplayResolution().ordinal());
dos.writeInt(settings.getAudioCodec().ordinal());
dos.flush();
return bos.toByteArray();
}
@@ -147,6 +148,10 @@ public final class CastProtocol {
readEnum(CastSettings.ReceiverDisplayResolution.values(), dis.readInt(),
CastSettings.ReceiverDisplayResolution.AUTO_FIT));
}
if (dis.available() > 0) {
settings.setAudioCodec(readEnum(CastSettings.AudioCodec.values(), dis.readInt(),
CastSettings.AudioCodec.AUTO));
}
} else {
dis.reset();
settings.setTransport(dis.readUTF());

View File

@@ -88,10 +88,13 @@ public final class CodecCatalog {
public static List<AudioCodecChoice> audioCodecChoicesForUi(CastSettings settings) {
List<AudioCodecChoice> out = new ArrayList<>();
boolean opus = NativeCodecBridge.isOpusAvailable();
boolean speex = NativeCodecBridge.isSpeexAvailable();
out.add(new AudioCodecChoice(CastSettings.AudioCodec.AUTO, true, 0));
out.add(scoredAudio(CastSettings.AudioCodec.AAC, true, settings));
out.add(scoredAudio(CastSettings.AudioCodec.OPUS, true, settings));
out.add(scoredAudio(CastSettings.AudioCodec.SPEEX, true, settings));
out.add(scoredAudio(CastSettings.AudioCodec.OPUS, opus, settings));
out.add(scoredAudio(CastSettings.AudioCodec.SPEEX, speex, settings));
out.add(scoredAudio(CastSettings.AudioCodec.PASSTHROUGH_DEBUG, true, settings));
sortAudioChoices(out);
return out;
}

View File

@@ -226,12 +226,13 @@
<string name="dev_reload_codecs_json">Перезагрузить codecs.json</string>
<string name="dev_reload_codecs_json_hint">Подгружает overrides из assets/codecs.json без перезапуска. Откройте настройки снова, чтобы обновить списки.</string>
<string name="codec_priority_score_suffix"> · %1$d</string>
<string name="label_audio_codec">Кодек для звука</string>
<string name="label_audio_codec">Аудио кодек</string>
<string name="codec_libvpx_vp8">libvpx (VP8)</string>
<string name="codec_libvpx_vp9">libvpx (VP9)</string>
<string name="codec_aac">AAC</string>
<string name="codec_speex">Speex</string>
<string name="codec_opus">Opus</string>
<string name="codec_audio_passthrough_debug">Passthrough (отладка)</string>
<string name="codec_audio_auto">AUTO (AAC)</string>
<string name="pip_enter">Картинка в картинке (PiP)</string>
<string name="pip_unavailable">Режим «картинка в картинке» (PiP) недоступен на этом устройстве</string>

View File

@@ -318,12 +318,13 @@
<string name="ota_download_failed">Download failed: %1$s</string>
<string name="ota_notification_channel_name">App updates</string>
<string name="codec_priority_score_suffix"> · %1$d</string>
<string name="label_audio_codec">Voice codec</string>
<string name="label_audio_codec">Audio codec</string>
<string name="codec_libvpx_vp8">libvpx (VP8)</string>
<string name="codec_libvpx_vp9">libvpx (VP9)</string>
<string name="codec_aac">AAC</string>
<string name="codec_speex">Speex</string>
<string name="codec_opus">Opus</string>
<string name="codec_audio_passthrough_debug">Passthrough (debug)</string>
<string name="codec_audio_auto">AUTO (AAC)</string>
<string name="pip_enter">Picture-in-picture</string>
<string name="pip_unavailable">Picture-in-picture is not available on this device</string>