Tunelowanie multimedialne

Tunelowanie multimedialne umożliwia tunelowanie skompresowanych danych wideo za pomocą sprzętu z dekodera wideo bezpośrednio na wyświetlacz, bez przetwarzania przez kod aplikacji Kod platformy Androida. Kod urządzenia poniżej stosu Androida określa, które klatki wideo mają być wysyłane na wyświetlacz i kiedy. porównując sygnatury czasowe prezentacji klatki wideo z jednym z poniższych: typy zegara wewnętrznego:

  • W przypadku odtwarzania filmów na żądanie w Androidzie 5 lub nowszym AudioTrack zegar zsynchronizowany z sygnaturami czasowymi prezentacji audio Zaliczono w aplikacji

  • Na potrzeby odtwarzania transmisji na żywo w Androidzie 11 lub nowszym: zegar referencyjny programu (PCR) lub zegara systemowego (STC) napędzanego przez tuner

Tło

w przypadku tradycyjnego odtwarzania filmów na Androidzie powiadomienia; w aplikacji po zdekodowaniu skompresowanej klatki wideo. Następnie aplikacja wersje zdekodowana klatka wideo na wyświetlacz, która będzie renderowana z tym samym zegarem systemowym. jako odpowiednią klatkę audio, pobieram dane historyczne AudioTimestamps aby obliczyć prawidłowy czas.

Ponieważ tunelowane odtwarzanie wideo omija kod aplikacji i zmniejsza liczbę przetwarzania wideo, może zapewnić efektywniejsze renderowanie filmu w zależności od wdrożenia OEM. Może też dostarczać dokładniejszych filmów częstotliwość i synchronizację z wybranym zegarem (PRC, STC lub audio) przez problemy z czasem działania spowodowane potencjalnym odchyleniem czasu wczytywania Androida żądań renderowania wideo i czasu prawdziwych sprzętowych vsync. Pamiętaj jednak: tunelowanie może też ograniczyć obsługę efektów GPU, takich jak: rozmycie lub zaokrąglonych rogów okna obrazu w obrazie, ponieważ ominąć stos graficzny Androida.

Na diagramie poniżej widać, jak tunelowanie upraszcza proces odtwarzania filmu.

Porównanie trybu tradycyjnego z trybem tunelu

Rysunek 1. Porównanie tradycyjnych i tunelowanych procesów odtwarzania wideo

Dla deweloperów aplikacji

Większość deweloperów aplikacji integruje się z biblioteką do odtwarzania lub implementacja, w większości przypadków wymaga jedynie ponownego skonfigurowania do odtwarzania tunelowego. Na potrzeby niskopoziomowej implementacji wideo tunelowanego postępuj zgodnie z poniższymi instrukcjami.

Aby odtwarzać filmy na żądanie w Androidzie 5 lub nowszym:

  1. Utwórz instancję SurfaceView.

  2. Utwórz instancję audioSessionId.

  3. Utwórz instancje AudioTrack i MediaCodec za pomocą interfejsu audioSessionId utworzonej w kroku 2.

  4. Dodawaj dane audio do kolejki AudioTrack z sygnaturą czasową prezentacji dla pierwszej klatki audio w danych audio.

Aby odtwarzać transmisję na żywo w Androidzie 11 lub nowszym:

  1. Utwórz instancję SurfaceView.

  2. Pobrać instancję avSyncHwId z usługi Tuner.

  3. Tworzenie instancji AudioTrack i MediaCodec za pomocą instancji avSyncHwId utworzonych w kroku 2.

Przepływ wywołań interfejsu API jest widoczny w tych fragmentach kodu:

aab.setContentType(AudioAttributes.CONTENT_TYPE_MOVIE);

// configure for audio clock sync
aab.setFlag(AudioAttributes.FLAG_HW_AV_SYNC);
// or, for tuner clock sync (Android 11 or higher)
new tunerConfig = TunerConfiguration(0, avSyncId);
aab.setTunerConfiguration(tunerConfig);
if (codecName == null) {
  return FAILURE;
}

// configure for audio clock sync
mf.setInteger(MediaFormat.KEY_AUDIO_SESSION_ID, audioSessionId);
// or, for tuner clock sync (Android 11 or higher)
mf.setInteger(MediaFormat.KEY_HARDWARE_AV_SYNC_ID, avSyncId);

Działanie odtwarzania filmów na żądanie

Ponieważ odtwarzanie wideo na żądanie jest domyślnie powiązane z funkcją AudioTrack działanie tunelowanego odtwarzania wideo może zależeć od jego działania w odtwarzaniu dźwięku.

  • Na większości urządzeń klatka wideo jest domyślnie renderowana dopiero po wyświetleniu dźwięku. rozpocznie się odtwarzanie. Możliwe jednak, że aplikacja będzie musiała wyrenderować klatkę wideo, rozpoczynanie odtwarzania dźwięku, na przykład w celu pokazania użytkownikowi bieżącego filmu pozycji podczas przewijania.

    • Żeby zasygnalizować, że pierwsza klatka filmu w kolejce powinna zostać wyrenderowana, gdy tylko zostanie wyrenderowana zostanie odkodowany, ustaw PARAMETER_KEY_TUNNEL_PEEK do 1. Gdy skompresowane klatki filmu są zmieniane w kolejce (na przykład podczas Klatki B są obecne). Oznacza to, że pierwsza wyświetlana klatka wideo powinna zawsze być iFrame.

    • Jeśli nie chcesz, by pierwsza klatka filmu znajdująca się w kolejce była renderowana do momentu pojawienia się dźwięku rozpocznie się odtwarzanie, ustaw ten parametr na 0.

    • Jeśli ten parametr nie jest skonfigurowany, sposób działania urządzenia określa producent OEM.

  • Gdy usługa AudioTrack nie otrzymuje danych audio, a bufory są puste (dźwięk niedostatecznie emitowany) odtwarzanie filmu się zatrzymuje do czasu zapisania kolejnych danych dźwiękowych. bo zegar audio przestał działać.

  • Podczas odtwarzania nieciągłości, których aplikacja nie może poprawić, mogą pojawić się sygnatury czasowe prezentacji audio. W takim przypadku OEM koryguje wartość ujemną. w postaci przerw w odtwarzaniu filmu, a także jako luki dodatnie, klatki wideo lub wstawianie cichych klatek audio (w zależności od modelu OEM implementacji). Położenie klatki na obrazie z AudioTimestamp nie zwiększa się w przypadku: wstawiono wyciszone klatki audio.

Producenci urządzeń

Konfiguracja

OEM powinien utworzyć oddzielny dekoder wideo do obsługi odtwarzania treści wideo tunelowanego. Taki dekoder powinien informować, że może on odtwarzać tunelowane Plik media_codecs.xml:

<Feature name="tunneled-playback" required="true"/>

Gdy tunelowana instancja MediaCodec zostanie skonfigurowana z identyfikatorem sesji audio, zapytanie AudioFlinger dla tego identyfikatora HW_AV_SYNC:

if (entry.getKey().equals(MediaFormat.KEY_AUDIO_SESSION_ID)) {
    int sessionId = 0;
    try {
        sessionId = (Integer)entry.getValue();
    }
    catch (Exception e) {
        throw new IllegalArgumentException("Wrong Session ID Parameter!");
    }
    keys[i] = "audio-hw-sync";
    values[i] = AudioSystem.getAudioHwSyncForSession(sessionId);
}

Podczas tego zapytania AudioFlinger pobiera identyfikator HW_AV_SYNC z głównego urządzenia audio i wewnętrznie kojarzy je z dźwiękiem. identyfikator sesji:

audio_hw_device_t *dev = mPrimaryHardwareDev->hwDevice();
char *reply = dev->get_parameters(dev, AUDIO_PARAMETER_HW_AV_SYNC);
AudioParameter param = AudioParameter(String8(reply));
int hwAVSyncId;
param.getInt(String8(AUDIO_PARAMETER_HW_AV_SYNC), hwAVSyncId);

Jeśli instancja AudioTrack została już utworzona, identyfikator HW_AV_SYNC to przekazywane do strumienia wyjściowego z tym samym identyfikatorem sesji audio. Jeśli nie został nie został jeszcze utworzony, to identyfikator HW_AV_SYNC jest przekazywany do strumienia wyjściowego podczas Kompozycja AudioTrack. Odbywa się to podczas odtwarzania wątek:

mOutput->stream->common.set_parameters(&mOutput->stream->common, AUDIO_PARAMETER_STREAM_HW_AV_SYNC, hwAVSyncId);

Identyfikator HW_AV_SYNC, niezależnie od tego, czy odpowiada on strumieniowi wyjściowemu audio czy Tuner, jest przekazywana do komponentu OMX lub Codec2, tak aby Kod OEM może powiązać kodek z odpowiednim strumieniem wyjściowym audio lub z tunera.

Podczas konfiguracji komponent OMX lub Codec2 powinien zwrócić błąd uchwyt boczny umożliwiający powiązanie kodeka z narzędziem Hardware Composer (HWC). Gdy aplikacja powiąże powierzchnię z elementem MediaCodec, ta opaska boczna jest przekazywany do HWC przez SurfaceFlinger, który konfiguruje jako pasek boczny.

err = native_window_set_sideband_stream(nativeWindow.get(), sidebandHandle);
if (err != OK) {
  ALOGE("native_window_set_sideband_stream(%p) failed! (err %d).", sidebandHandle, err);
  return err;
}

HWC odpowiada za odbiór nowych buforów obrazu z danych wyjściowych kodeka odpowiedni czas synchronizacji z powiązanym strumieniem wyjścia audio lub zegar referencyjny programu dostrajania, komponując bufory z bieżącym czasem zawartości innych warstw i wyświetlając wynikowy obraz. Dzieje się tak niezależnie od normalnego cyklu przygotowań i stawek. Przygotowanie i ustawianie rozmów zachodzi tylko wtedy, gdy zmienią się inne warstwy lub właściwości warstwy paska bocznego (np. pozycji lub rozmiaru).

OMX,

Komponent dekodera tunelowego powinien obsługiwać:

  • Ustawiam: OMX.google.android.index.configureVideoTunnelMode który wykorzystuje strukturę ConfigureVideoTunnelModeParams do przekazywania w identyfikatorze HW_AV_SYNC powiązanym z urządzeniem wyjściowym audio.

  • Skonfigurowanie parametru OMX_IndexConfigAndroidTunnelPeek, który informuje kodek ma być renderowany lub nie renderować pierwszej zdekodowanej klatki wideo, niezależnie od czy rozpoczęło się odtwarzanie dźwięku.

  • Wysyłam zdarzenie OMX_EventOnFirstTunnelFrameReady po pierwszym tunelowaniu klatka wideo została zdekodowana i jest gotowa do renderowania.

Implementacja AOSP konfiguruje tryb tunelu w ACodec Przez OMXNodeInstance Jak widać w tym fragmencie kodu:

OMX_INDEXTYPE index;
OMX_STRING name = const_cast<OMX_STRING>(
        "OMX.google.android.index.configureVideoTunnelMode");

OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index);

ConfigureVideoTunnelModeParams tunnelParams;
InitOMXParams(&tunnelParams);
tunnelParams.nPortIndex = portIndex;
tunnelParams.bTunneled = tunneled;
tunnelParams.nAudioHwSync = audioHwSync;
err = OMX_SetParameter(mHandle, index, &tunnelParams);
err = OMX_GetParameter(mHandle, index, &tunnelParams);
sidebandHandle = (native_handle_t*)tunnelParams.pSidebandWindow;

Jeśli komponent obsługuje tę konfigurację, powinien przydzielić pasek boczny w tym kodeku i przekazać go elementowi pSidebandWindow, że HWC może zidentyfikować powiązany kodek. Jeśli komponent nie obsługuje tę konfigurację, ustaw wartość bTunneled na OMX_FALSE.

Kodek2

W Androidzie 11 lub nowszym Codec2 obsługuje odtwarzanie tunelowane. Dekoder powinien obsługiwać te komponenty:

  • Konfiguruję instancję C2PortTunneledModeTuning, która konfiguruje tryb tunelu przechodzi w HW_AV_SYNC pobranym z urządzenia wyjściowego audio lub konfiguracji tunera.

  • Wysłanie zapytania do funkcji C2_PARAMKEY_OUTPUT_TUNNEL_HANDLE w celu przydzielenia i pobrania uchwytu paska bocznego dla urządzeń HWC.

  • Obsługa C2_PARAMKEY_TUNNEL_HOLD_RENDER po podłączeniu do C2Work, które instruuje kodek, aby dekodował i sygnalizował zakończenie pracy, ale nie renderował w buforze wyjściowym do momentu 1) kodeka zostanie później zlecona lub 2) rozpocznie się odtwarzanie dźwięku.

  • Obsługa metody C2_PARAMKEY_TUNNEL_START_RENDER, która instruuje kodek natychmiast wyrenderuj ramkę oznaczoną znakiem C2_PARAMKEY_TUNNEL_HOLD_RENDER, nawet jeśli odtwarzanie dźwięku jeszcze się nie rozpoczęło.

  • Pozostaw usługę debug.stagefright.ccodec_delayed_params nieskonfigurowaną (zalecane). Jeśli go skonfigurować, ustaw wartość false.

Implementacja AOSP konfiguruje tryb tunelu w CCodec do C2PortTunnelModeTuning, jak widać w tym fragmencie kodu:

if (msg->findInt32("audio-hw-sync", &tunneledPlayback->m.syncId[0])) {
    tunneledPlayback->m.syncType =
            C2PortTunneledModeTuning::Struct::sync_type_t::AUDIO_HW_SYNC;
} else if (msg->findInt32("hw-av-sync-id", &tunneledPlayback->m.syncId[0])) {
    tunneledPlayback->m.syncType =
            C2PortTunneledModeTuning::Struct::sync_type_t::HW_AV_SYNC;
} else {
    tunneledPlayback->m.syncType =
            C2PortTunneledModeTuning::Struct::sync_type_t::REALTIME;
    tunneledPlayback->setFlexCount(0);
}
c2_status_t c2err = comp->config({ tunneledPlayback.get() }, C2_MAY_BLOCK,
        failures);
std::vector<std::unique_ptr<C2Param>> params;
c2err = comp->query({}, {C2PortTunnelHandleTuning::output::PARAM_TYPE},
        C2_DONT_BLOCK, &params);
if (c2err == C2_OK && params.size() == 1u) {
    C2PortTunnelHandleTuning::output *videoTunnelSideband =
            C2PortTunnelHandleTuning::output::From(params[0].get());
    return OK;
}

Jeśli komponent obsługuje tę konfigurację, powinien przydzielić pasek boczny do tego kodeka i przekazać go za pomocą C2PortTunnelHandlingTuning, że HWC może zidentyfikować powiązany kodek.

HAL audio

W przypadku odtwarzania filmów na żądanie HAL audio odbiera prezentację audio. Znaleziono sygnatury czasowe w nagłówku wewnątrz danych audio w formacie big-endian na początku każdego bloku danych dźwiękowych, które aplikacja zapisuje:

struct TunnelModeSyncHeader {
  // The 32-bit data to identify the sync header (0x55550002)
  int32 syncWord;
  // The size of the audio data following the sync header before the next sync
  // header might be found.
  int32 sizeInBytes;
  // The presentation timestamp of the first audio sample following the sync
  // header.
  int64 presentationTimestamp;
  // The number of bytes to skip after the beginning of the sync header to find the
  // first audio sample (20 bytes for compressed audio, or larger for PCM, aligned
  // to the channel count and sample size).
  int32 offset;
}

Aby HWC renderowało klatki wideo zsynchronizowane z odpowiednimi klatkami audio, HAL audio powinien przeanalizować nagłówek synchronizacji i użyć sygnatury czasowej prezentacji do zsynchronizować zegar odtwarzania z renderowaniem dźwięku. Aby przeprowadzić ponowną synchronizację, gdy W trakcie odtwarzania skompresowanego dźwięku może być konieczne przeanalizowanie metadanych przez HAL audio w skompresowanych danych audio, aby określić czas odtwarzania.

Wstrzymaj pomoc

Android 5 i starsze wersje nie obsługują wstrzymywania. Możesz wstrzymać tunelowanie z powodu braku sygnału audio, ale jeśli wewnętrzny bufor dla filmu jest duży (na przykład w komponencie OMX jest jedna sekunda danych), powoduje wstrzymanie nie odpowiada.

W Androidzie 5.1 lub nowszym aplikacja AudioFlinger obsługuje wstrzymywanie i wznawianie przesyłania bezpośredniego (tunelowanych) wyjść audio. Jeśli HAL stosuje wstrzymywanie i wznawianie, śledź wstrzymanie a CV jest przekazywane do HAL.

Sekwencja wstrzymywania, opróżniania i wznowienia jest respektowana przez wykonywanie wywołań HAL. w wątku odtwarzania (tak samo jak w przypadku odciążania).

Sugestie dotyczące implementacji

HAL audio

W Androidzie 11 identyfikator synchronizacji sprzętu z PCR lub STC może być używany do synchronizacji audio/wideo, jest obsługiwany tylko strumień wideo.

W przypadku Androida 10 lub starszego urządzenia obsługujące odtwarzanie filmów z tunelem powinny mieć co najmniej 1 profil strumienia wyjściowego audio z parametrami FLAG_HW_AV_SYNC i AUDIO_OUTPUT_FLAG_DIRECT w pliku audio_policy.conf. Te flagi służą do ustawiania zegara systemowego na poziomie zegara audio.

OMX,

Producenci urządzeń powinni mieć osobny komponent OMX dla wideo tunelowanego na potrzeby innych typów systemów uczących się (producenci mogą mieć dodatkowe komponenty OMX dla innych typów odtwarzania dźwięku i wideo, np. bezpieczne odtwarzanie). Komponent tunelowany powinien:

  • Określ 0 buforów (nBufferCountMin, nBufferCountActual) na danych wyjściowych portu.

  • Zaimplementuj rozszerzenie OMX.google.android.index.prepareForAdaptivePlayback setParameter.

  • Określ jej możliwości w pliku media_codecs.xml i zadeklaruj komponent dzięki funkcji odtwarzania tunelowanego. Powinny również zawierać informacje o ograniczeniach dotyczących ramki. rozmiar, wyrównanie czy szybkość transmisji bitów. Przykład poniżej:

    <MediaCodec name="OMX.OEM_NAME.VIDEO.DECODER.AVC.tunneled"
    type="video/avc" >
        <Feature name="adaptive-playback" />
        <Feature name="tunneled-playback" required=”true” />
        <Limit name="size" min="32x32" max="3840x2160" />
        <Limit name="alignment" value="2x2" />
        <Limit name="bitrate" range="1-20000000" />
            ...
    </MediaCodec>
    

Jeśli do obsługi dekodowania tunelowanego i nietunelu używany jest ten sam komponent OMX, funkcja odtwarzania tunelowanego powinna pozostać niewymagana. Zarówno tunelowane, dekodery nietunele mają te same ograniczenia możliwości. Na przykład: poniżej:

<MediaCodec name="OMX._OEM\_NAME_.VIDEO.DECODER.AVC" type="video/avc" >
    <Feature name="adaptive-playback" />
    <Feature name="tunneled-playback" />
    <Limit name="size" min="32x32" max="3840x2160" />
    <Limit name="alignment" value="2x2" />
    <Limit name="bitrate" range="1-20000000" />
        ...
</MediaCodec>

Hardware Composer (HWC)

Gdy włączona jest warstwa tunelowa (warstwa z atrybutem HWC_SIDEBAND compositionType) wyświetlacza, sidebandStream warstwy to uchwyt paska bocznego przydzielony przez Komponent wideo OMX.

HWC synchronizuje zdekodowane klatki wideo (z tunelowanego komponentu OMX) z powiązana ścieżka audio (z identyfikatorem audio-hw-sync). Gdy nowa klatka wideo jest wyświetlana staje się aktualny, HWC łączy go z bieżącą zawartością wszystkich warstw podczas ostatniego przygotowania lub ustawionej rozmowy, i wyświetla wynikowy obraz. Wywołania przygotowujące lub ustawione następują tylko wtedy, gdy zmienią się inne warstwy albo właściwości warstwy paska bocznego (takie jak pozycja i rozmiar).

Poniższy rysunek przedstawia kod HWC współpracujący ze sprzętem (lub jądrem sterownika) w celu połączenia klatek wideo (7b) z najnowszą kompozycją (7a) do wyświetlania w odpowiednim momencie na podstawie dźwięku (7c).

HWC łączenie klatek wideo na podstawie dźwięku

Rysunek 2. Synchronizator sprzętu HWC (lub jądra lub sterownika)