Wyświetlanie multimediów

Interfejsy API android.media.projection wprowadzone w Androidzie 5 (poziom interfejsu API 21) umożliwiają rejestrowanie zawartości ekranu urządzenia w formie strumienia multimediów, który możesz odtwarzać, nagrywać i przesyłać na inne urządzenia, takie jak telewizory.

Android 14 (poziom interfejsu API 34) wprowadza udostępnianie ekranu aplikacji, które umożliwia użytkownikom udostępnianie pojedynczego okna aplikacji zamiast całego ekranu urządzenia niezależnie od trybu okna. Udostępnianie ekranu aplikacji wyklucza pasek stanu, pasek nawigacyjny, powiadomienia i inne elementy interfejsu systemu ze współdzielonego wyświetlacza – nawet wtedy, gdy aplikacja jest używana do przechwytywania ekranu na pełnym ekranie. Udostępniana jest tylko zawartość wybranej aplikacji.

Udostępnianie ekranu aplikacji zapewnia prywatność użytkowników, zwiększa ich produktywność i ułatwia wielozadaniowość, umożliwiając użytkownikom korzystanie z wielu aplikacji, ale ograniczając udostępnianie treści tylko do jednej aplikacji.

3 reprezentacje displayowe

Wyświetlanie multimediów rejestruje zawartość wyświetlacza urządzenia lub okna aplikacji, a następnie przesyła przechwycony obraz na wirtualny wyświetlacz, który renderuje obraz na urządzeniu Surface.

Wyświetlacz urządzenia rzeczywistego wyświetlany na wyświetlaczu wirtualnym. Zawartość wirtualnego wyświetlacza zapisanego w udostępnianej przez aplikację usłudze „Surface”.
Rysunek 1. Ekran rzeczywistego urządzenia lub okno aplikacji wyświetlane na wyświetlaczu wirtualnym. Wirtualny wyświetlacz zapisany w udostępnionym przez aplikację Surface.

Aplikacja udostępnia Surface za pomocą MediaRecorder, SurfaceTexture lub ImageReader, który pobiera zawartość przechwyconego wyświetlacza i umożliwia zarządzanie obrazami renderowanymi na urządzeniu Surface w czasie rzeczywistym. Możesz zapisać obrazy jako nagrania lub przesłać je na telewizor lub inne urządzenie.

Rzeczywista sieć reklamowa

Rozpocznij sesję wyświetlania multimediów, uzyskując token, który umożliwia aplikacji przechwytywanie zawartości wyświetlacza urządzenia lub okna aplikacji. Token jest reprezentowany przez instancję klasy MediaProjection.

Użyj metody getMediaProjection() usługi systemowej MediaProjectionManager, aby utworzyć instancję MediaProjection podczas uruchamiania nowego działania. Rozpocznij działanie z intencją używaną w metodzie createScreenCaptureIntent(), aby określić operację przechwytywania ekranu:

Kotlin

val mediaProjectionManager = getSystemService(MediaProjectionManager::class.java)
var mediaProjection : MediaProjection

val startMediaProjection = registerForActivityResult(
    StartActivityForResult()
) { result ->
    if (result.resultCode == RESULT_OK) {
        mediaProjection = mediaProjectionManager
            .getMediaProjection(result.resultCode, result.data!!)
    }
}

startMediaProjection.launch(mediaProjectionManager.createScreenCaptureIntent())

Java

final MediaProjectionManager mediaProjectionManager =
    getSystemService(MediaProjectionManager.class);
final MediaProjection[] mediaProjection = new MediaProjection[1];

ActivityResultLauncher<Intent> startMediaProjection = registerForActivityResult(
    new StartActivityForResult(),
    result -> {
        if (result.getResultCode() == Activity.RESULT_OK) {
            mediaProjection[0] = mediaProjectionManager
                .getMediaProjection(result.getResultCode(), result.getData());
        }
    }
);

startMediaProjection.launch(mediaProjectionManager.createScreenCaptureIntent());

Wyświetlacz wirtualny

Centralnym elementem projekcji multimediów jest wirtualny wyświetlacz, który możesz utworzyć, wywołując metodę createVirtualDisplay() w instancji MediaProjection:

Kotlin

virtualDisplay = mediaProjection.createVirtualDisplay(
                     "ScreenCapture",
                     width,
                     height,
                     screenDensity,
                     DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                     surface,
                     null, null)

Java

virtualDisplay = mediaProjection.createVirtualDisplay(
                     "ScreenCapture",
                     width,
                     height,
                     screenDensity,
                     DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                     surface,
                     null, null);

Parametry width i height określają wymiary wyświetlacza wirtualnego. Aby uzyskać wartości szerokości i wysokości, użyj interfejsów API WindowMetrics wprowadzonych w Androidzie 11 (poziom API 30). Szczegółowe informacje znajdziesz w sekcji Rozmiar odwzorowania multimediów.

Powierzchnia

Dopasuj rozmiar powierzchni projekcji multimediów, aby uzyskać obraz w odpowiedniej rozdzielczości. Zadbaj o dużą (niską rozdzielczość) powierzchnię do przesyłania ekranu na telewizory lub monitory komputerowe, a także na małą (dużą rozdzielczość) w celu nagrywania ekranu urządzenia.

Od Androida 12L (poziom interfejsu API 32) podczas renderowania przechwyconych treści na powierzchni system skaluje treści równomiernie z zachowaniem współczynnika proporcji, tak aby oba wymiary (szerokość i wysokość) były równe lub mniejsze od odpowiednich wymiarów powierzchni. Zrobione nagranie jest wyśrodkowane na powierzchni.

Skalowanie Androida 12L usprawnia przesyłanie ekranu na telewizory i inne duże ekrany, maksymalizując rozmiar obrazu powierzchni i zapewniając odpowiedni współczynnik proporcji.

Uprawnienia usługi na pierwszym planie

Jeśli Twoja aplikacja jest kierowana na Androida 14 lub nowszego, jej plik manifestu musi zawierać deklarację uprawnień dla usługi na pierwszym planie typu mediaProjection:

<manifest ...>
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
    <application ...>
        <service
            android:name=".MyMediaProjectionService"
            android:foregroundServiceType="mediaProjection"
            android:exported="false">
        </service>
    </application>
</manifest>

Uruchom usługę wyświetlania multimediów przez wywołanie metody startForeground().

Jeśli w wywołaniu nie określisz typu usługi na pierwszym planie, zostanie on domyślnie ustawiony na bitową liczbę całkowitą tych typów usług na pierwszym planie zdefiniowanych w pliku manifestu. Jeśli plik manifestu nie określa żadnych typów usług, system zgłasza MissingForegroundServiceTypeException.

Twoja aplikacja musi prosić użytkownika o zgodę przed każdą sesją wyświetlania multimediów. Sesja to pojedyncze wywołanie funkcji createVirtualDisplay(). Tokenu MediaProjection można użyć tylko raz do wywołania.

Na Androidzie 14 lub nowszym metoda createVirtualDisplay() generuje SecurityException, jeśli aplikacja wykona jedną z tych czynności:

  • Przekazuje instancję Intent zwróconą z createScreenCaptureIntent() do getMediaProjection() więcej niż raz
  • Wywołuje createVirtualDisplay() więcej niż raz w tej samej instancji MediaProjection

Rozmiar projekcji multimediów

Wyświetlanie multimediów może obejmować cały ekran urządzenia lub okno aplikacji niezależnie od trybu okna.

Rozmiar początkowy

W przypadku wyświetlania multimediów na pełnym ekranie aplikacja musi określać rozmiar ekranu urządzenia. W przypadku udostępniania ekranu w aplikacji aplikacja nie będzie mogła określić rozmiaru nagrywanego wyświetlacza, dopóki użytkownik nie wybierze regionu przechwytywania. Początkowy rozmiar każdej projekcji multimediów to rozmiar ekranu urządzenia.

Użyj metody platformy WindowManager getMaximumWindowMetrics(), aby zwrócić obiekt WindowMetrics dla ekranu urządzenia, nawet jeśli aplikacja hosta wyświetlania multimediów działa w trybie wielu okien i zajmuje tylko część wyświetlacza.

Aby zapewnić zgodność do poziomu interfejsu API 14, użyj metody WindowMetricsCalculator computeMaximumWindowMetrics() z biblioteki Jetpack WindowManager.

Wywołaj metodę WindowMetrics getBounds(), aby uzyskać szerokość i wysokość wyświetlacza urządzenia.

Zmiany rozmiaru

Rozmiar projekcji multimediów może się zmienić, gdy urządzenie zostanie obrócone lub użytkownik wybierze okno aplikacji jako region przechwytywania ekranu w ramach udostępniania ekranu aplikacji. W wyświetlaniu multimediów mogą być wyświetlane czarne pasy, jeśli rozmiar rejestrowanych treści różni się od maksymalnych wskaźników okien uzyskanych podczas konfigurowania odwzorowania multimediów.

Aby mieć pewność, że projekcja multimediów dokładnie pasuje do rozmiaru zarejestrowanych treści w przechwyconych obszarach i w przypadku różnych obrotów na urządzeniach, użyj wywołania zwrotnego onCapturedContentResize() do zmiany rozmiaru zapisu. Więcej informacji znajdziesz w sekcji Dostosowywanie poniżej.

Personalizacja

Twoja aplikacja może dostosować interfejs wyświetlania multimediów za pomocą tych interfejsów API MediaProjection.Callback:

  • onCapturedContentVisibilityChanged(): pozwala aplikacji hosta (aplikacji, która rozpoczęła projekcję multimediów) w celu wyświetlania lub ukrywania udostępnionych treści.

    Za pomocą tego wywołania zwrotnego możesz dostosować interfejs aplikacji do tego, czy przechwycony region jest widoczny dla użytkownika. Jeśli np. aplikacja jest widoczna dla użytkownika i wyświetla przechwycone treści w jej interfejsie, a przechwycona aplikacja jest również widoczna dla użytkownika (co wskazuje to wywołanie zwrotne), użytkownik zobaczy tę samą treść 2 razy. Wywołanie zwrotne pozwala zaktualizować interfejs aplikacji i ukryć przechwycone treści oraz zwolnić miejsce w układzie na inne treści.

  • onCapturedContentResize(): umożliwia aplikacji hosta zmianę rozmiaru projekcji multimediów na wirtualnym wyświetlaczu i projekcie multimediów Surface w zależności od rozmiaru przechwyconego regionu wyświetlania.

    Wywoływane przy każdej zmianie rozmiaru przechwyconej treści (pojedynczego okna aplikacji lub wyświetlacza na pełnym urządzeniu) (z powodu obrotu urządzenia lub przejścia aplikacji w inny tryb okna). Użyj tego interfejsu API, aby zmienić rozmiar zarówno wirtualnego wyświetlacza, jak i wirtualnego wyświetlacza, tak aby współczynnik proporcji odpowiadał nagrywanej treści i nie było widać na filmie czarnych pasów.

Odzyskiwanie zasobów

Aplikacja powinna zarejestrować wywołanie zwrotne MediaProjection onStop(), aby zwolnić zasoby przechowywane przez aplikację, takie jak wirtualny wyświetlacz i powierzchnia do wyświetlania.

Wywołanie zwrotne jest wywoływane po zakończeniu projekcji multimediów lub gdy użytkownik nie wyrazi zgody na kontynuowanie sesji nagrywania.

Jeśli aplikacja nie rejestruje wywołania zwrotnego, a użytkownik nie wyrazi zgody na sesję projekcji multimediów, wywołania funkcji createVirtualDisplay() powodują zgłoszenie IllegalStateException.

Zrezygnuj

Android w wersji 14 lub nowszej domyślnie umożliwia udostępnianie ekranu aplikacji. W przypadku każdej sesji projekcji multimediów użytkownicy mogą udostępnić okno aplikacji lub cały wyświetlacz.

Aplikacja może zrezygnować z udostępniania ekranu, wywołując metodę createScreenCaptureIntent(MediaProjectionConfig) z argumentem MediaProjectionConfig zwróconym z wywołania funkcji createConfigForDefaultDisplay().

Wywołanie funkcji createScreenCaptureIntent(MediaProjectionConfig) z argumentem MediaProjectionConfig zwróconym przez wywołanie createConfigForUserChoice() jest takie samo jak działanie domyślne, czyli wywołanie createScreenCaptureIntent().

Aplikacje z możliwością zmiany rozmiaru

Zawsze włączaj możliwość zmiany rozmiaru w aplikacjach do wyświetlania multimediów (resizeableActivity="true"). Aplikacje z możliwością zmiany rozmiaru obsługują zmiany konfiguracji urządzeń oraz tryb wielu okien (patrz Obsługa wielu okien).

Jeśli aplikacji nie można zmienić rozmiaru, musi ona wysyłać zapytanie o granic wyświetlania z kontekstu okna i za pomocą funkcji getMaximumWindowMetrics() pobrać WindowMetrics maksymalnego obszaru wyświetlania dostępnego dla aplikacji :

Kotlin

val windowContext = context.createWindowContext(context.display!!,
      WindowManager.LayoutParams.TYPE_APPLICATION, null)
val projectionMetrics = windowContext.getSystemService(WindowManager::class.java)
      .maximumWindowMetrics

Java

Context windowContext = context.createWindowContext(context.getDisplay(),
      WindowManager.LayoutParams.TYPE_APPLICATION, null);
WindowMetrics projectionMetrics = windowContext.getSystemService(WindowManager.class)
      .getMaximumWindowMetrics();

Dodatkowe materiały

Więcej informacji o wyświetlaniu multimediów znajdziesz w artykule Nagrywanie filmów i odtwarzanie dźwięku.