מנהור מולטימדיה

מנהור מולטימדיה מאפשר לנתוני וידאו דחוסים לעבור דרך מפענח וידאו חומרה ישירות לתצוגה, ללא עיבוד על ידי קוד אפליקציה או קוד מסגרת אנדרואיד. הקוד הספציפי למכשיר מתחת למחסנית אנדרואיד קובע אילו מסגרות וידיאו לשלוח לתצוגה ומתי לשלוח אותן על ידי השוואת חותמות הזמן של הצגת מסגרת הווידאו עם אחד מסוגי השעון הפנימי הבאים:

  • להפעלת וידאו לפי דרישה באנדרואיד 5 ומעלה, שעון AudioTrack המסונכרן עם חותמות זמן של מצגת אודיו המועברת על ידי האפליקציה

  • להשמעת שידור חי באנדרואיד 11 ומעלה, שעון התייחסות לתוכנית (PCR) או שעון זמן מערכת (STC) המונע על ידי טיונר

רקע כללי

הפעלת וידאו מסורתית באנדרואיד מודיעה לאפליקציה כאשר מסגרת וידאו דחוסה פוענחה. לאחר מכן, האפליקציה משחררת את מסגרת הווידאו המפוענחת לתצוגה כדי להיות מוצגת באותו זמן שעון מערכת כמו מסגרת האודיו המתאימה, מאחזרת מופעי AudioTimestamps היסטוריים כדי לחשב את התזמון הנכון.

מכיוון שהפעלת וידאו במנהור עוקפת את קוד האפליקציה ומפחיתה את מספר התהליכים הפועלים על הסרטון, היא יכולה לספק עיבוד וידאו יעיל יותר בהתאם ליישום OEM. זה גם יכול לספק קצב ווידאו מדויק יותר וסנכרון לשעון הנבחר (PRC, STC או אודיו) על ידי הימנעות מבעיות תזמון שנוצרות עקב הטיה פוטנציאלית בין התזמון של בקשות אנדרואיד לעיבוד וידאו, לבין התזמון של vsyncs אמיתי של חומרה. עם זאת, מנהור יכול גם להפחית את התמיכה באפקטים של GPU כגון טשטוש או פינות מעוגלות בחלונות תמונה-בתמונה (PiP), מכיוון שהמאגרים עוקפים את ערימת הגרפיקה של אנדרואיד.

התרשים הבא מראה כיצד מנהור מפשט את תהליך הפעלת הווידאו.

השוואה בין מסורת ואופני מנהרה

איור 1. השוואה בין תהליכי השמעת וידאו מסורתיים ומנוהרים

למפתחי אפליקציות

מכיוון שרוב מפתחי האפליקציות משתלבים עם ספרייה לצורך הטמעת השמעה, ברוב המקרים הטמעה דורשת רק הגדרה מחדש של ספרייה זו עבור השמעה במנהור. להטמעה ברמה נמוכה של נגן וידאו עם מנהור, השתמש בהוראות הבאות.

להפעלת וידאו לפי דרישה באנדרואיד 5 ומעלה:

  1. צור מופע SurfaceView .

  2. צור מופע audioSessionId .

  3. צור מופעי AudioTrack ו- MediaCodec עם מופע audioSessionId שנוצר בשלב 2.

  4. תור נתוני אודיו ל- AudioTrack עם חותמת הזמן של המצגת עבור מסגרת האודיו הראשונה בנתוני האודיו.

להפעלת שידור חי באנדרואיד 11 ומעלה:

  1. צור מופע SurfaceView .

  2. קבל מופע avSyncHwId מ- Tuner .

  3. צור מופעי AudioTrack ו- MediaCodec עם מופע avSyncHwId שנוצר בשלב 2.

זרימת הקריאה ל-API מוצגת בקטעי הקוד הבאים:

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);

התנהגות של הפעלת וידאו לפי דרישה

מכיוון שהפעלת וידאו במנהור לפי דרישה קשורה באופן מרומז להפעלת AudioTrack , ההתנהגות של הפעלת וידאו במנהור עשויה להיות תלויה בהתנהגות השמעת אודיו.

  • ברוב המכשירים, כברירת מחדל, מסגרת וידאו לא מעובדת עד שמתחיל השמעת אודיו. עם זאת, ייתכן שהאפליקציה תצטרך לרנדר מסגרת וידאו לפני תחילת השמעת אודיו, למשל, כדי להראות למשתמש את מיקום הווידאו הנוכחי בזמן החיפוש.

    • כדי לאותת שיש להציג את מסגרת הווידאו הראשונה בתור ברגע שהוא מפוענח, הגדר את הפרמטר PARAMETER_KEY_TUNNEL_PEEK ל 1 . כאשר מסגרות וידאו דחוסות מסודרות מחדש בתור (כגון כאשר קיימים B-frames ), פירוש הדבר שהפריים הראשון המוצג צריך להיות תמיד I-frame.

    • אם אינך רוצה שמסגרת הווידאו הראשונה בתור תוצג עד שתתחיל השמעת אודיו, הגדר פרמטר זה ל 0 .

    • אם פרמטר זה לא מוגדר, ה-OEM קובע את התנהגות ההתקן.

  • כאשר נתוני אודיו אינם מסופקים ל- AudioTrack והמאגרים ריקים (לא הפעלת אודיו), הפעלת הווידאו נעצרת עד שנכתבים יותר נתוני אודיו מכיוון ששעון האודיו אינו מתקדם יותר.

  • במהלך השמעה, חוסר המשכיות שהאפליקציה לא יכולה לתקן עשויות להופיע בחותמות זמן של מצגת אודיו. כאשר זה קורה, ה-OEM מתקן פערים שליליים על ידי עצירת מסגרת הווידאו הנוכחית, ופערים חיוביים על ידי ביטול פריימים של וידאו או הוספת מסגרות אודיו שקטות (בהתאם למימוש ה-OEM). מיקום המסגרת AudioTimestamp אינו עולה עבור מסגרות אודיו שקטות שהוכנסו.

עבור יצרני מכשירים

תְצוּרָה

יצרני OEM צריכים ליצור מפענח וידאו נפרד כדי לתמוך בהפעלת וידאו במנהור. המפענח הזה צריך לפרסם שהוא מסוגל להפעיל מנהור בקובץ media_codecs.xml :

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

כאשר מופע MediaCodec עם מנהור מוגדר עם מזהה הפעלת אודיו, הוא שואל את AudioFlinger עבור מזהה 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);
}

במהלך שאילתה זו, AudioFlinger מאחזר את מזהה HW_AV_SYNC מהתקן האודיו הראשי ומשייך אותו באופן פנימי למזהה הפעלת האודיו:

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);

אם כבר נוצר מופע AudioTrack , מזהה HW_AV_SYNC מועבר לזרם הפלט עם אותו מזהה הפעלת אודיו. אם הוא עדיין לא נוצר, מזהה HW_AV_SYNC מועבר לזרם הפלט במהלך יצירת AudioTrack . זה נעשה על ידי שרשור ההשמעה :

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

מזהה HW_AV_SYNC , בין אם הוא מתאים לזרם פלט אודיו או לתצורת Tuner , מועבר לרכיב OMX או Codec2 כך שקוד ה-OEM יכול לשייך את ה-Codec לזרם פלט האודיו המתאים או לזרם הטיונר.

במהלך תצורת הרכיב, רכיב OMX או Codec2 אמור להחזיר ידית פס צד שניתן להשתמש בה כדי לשייך את ה-Codec לשכבת Hardware Composer (HWC). כאשר האפליקציה משייכת משטח ל- MediaCodec , ידית פס הצד הזו מועברת ל-HWC דרך SurfaceFlinger , שמגדירה את השכבה כשכבת פס צד .

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 אחראית על קבלת מאגרי תמונה חדשים מפלט ה-codec בזמן המתאים, בין אם מסונכרנים לזרם פלט האודיו המשויך או לשעון ההתייחסות של תוכנית הטיונר, חיבור המאגרים עם התוכן הנוכחי של שכבות אחרות והצגת התמונה המתקבלת. זה קורה ללא תלות במחזור ההכנה וההגדרה הרגיל. קריאות ההכנה וההגדרה מתרחשות רק כאשר שכבות אחרות משתנות, או כאשר המאפיינים של שכבת פס הצד (כגון מיקום או גודל) משתנים.

OMX

רכיב מפענח עם מנהור אמור לתמוך בדברים הבאים:

  • הגדרת הפרמטר המורחב OMX.google.android.index.configureVideoTunnelMode , המשתמש במבנה ConfigureVideoTunnelModeParams כדי להעביר את מזהה HW_AV_SYNC המשויך להתקן פלט האודיו.

  • קביעת התצורה של הפרמטר OMX_IndexConfigAndroidTunnelPeek שאומר ל-Codec לרנדר או לא לרנדר את מסגרת הווידאו המפוענחת הראשונה, ללא קשר אם החלה השמעת אודיו.

  • שליחת אירוע OMX_EventOnFirstTunnelFrameReady כאשר פריים הווידאו המנהור הראשון פוענח ומוכן לעיבוד.

יישום AOSP מגדיר את מצב המנהרה ב- ACodec דרך OMXNodeInstance כפי שמוצג בקטע הקוד הבא:

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;

אם הרכיב תומך בתצורה זו, עליו להקצות ידית פס צד ל-codec זה ולהעביר אותו בחזרה דרך חבר pSidebandWindow כך שה-HWC יוכל לזהות את ה-codec המשויך. אם הרכיב אינו תומך בתצורה זו, עליו להגדיר bTunneled ל- OMX_FALSE .

Codec2

באנדרואיד 11 ומעלה, Codec2 תומך בהפעלה במנהור. רכיב המפענח צריך לתמוך בדברים הבאים:

  • קביעת תצורה C2PortTunneledModeTuning , המגדיר את מצב המנהרה ועובר ב- HW_AV_SYNC מאוחזר מהתקן פלט השמע או מתצורת הטיונר.

  • שאילתה C2_PARAMKEY_OUTPUT_TUNNEL_HANDLE , כדי להקצות ולאחזר את ידית פס הצד עבור HWC.

  • טיפול ב- C2_PARAMKEY_TUNNEL_HOLD_RENDER כאשר הוא מחובר ל- C2Work , אשר מורה ל-Codec לפענח ולאותת על השלמת העבודה, אך לא לעבד את מאגר הפלט עד ש-1) ה-Codec יקבל הוראה מאוחר יותר לרנדר אותו או 2) הפעלת שמע תתחיל.

  • טיפול C2_PARAMKEY_TUNNEL_START_RENDER , המורה ל-Codec לעבד מיד את המסגרת שסומנה ב- C2_PARAMKEY_TUNNEL_HOLD_RENDER , גם אם הפעלת האודיו לא התחילה.

  • השאר את debug.stagefright.ccodec_delayed_params ללא תצורה (מומלץ). אם תגדיר אותו, הגדר ל- false .

יישום AOSP מגדיר את מצב המנהרה ב CCodec דרך C2PortTunnelModeTuning , כפי שמוצג בקטע הקוד הבא:

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;
}

אם הרכיב תומך בתצורה זו, עליו להקצות ידית פס צד ל-codec זה ולהעביר אותו בחזרה דרך C2PortTunnelHandlingTuning כך שה-HWC יוכל לזהות את ה-codec המשויך.

אודיו HAL

להפעלת וידאו לפי דרישה, ה- Audio HAL מקבל את חותמות הזמן של מצגת האודיו בשורה אחת עם נתוני האודיו בפורמט גדול-אנדי בתוך כותרת שנמצאת בתחילת כל בלוק של נתוני אודיו שהאפליקציה כותבת:

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;
}

כדי ש-HWC יעבד מסגרות וידאו בסנכרון עם מסגרות האודיו המתאימות, ה- Audio HAL צריך לנתח את כותרת הסנכרון ולהשתמש בחותמת הזמן של המצגת כדי לסנכרן מחדש את שעון ההשמעה עם עיבוד אודיו. כדי לסנכרן מחדש כאשר מושמע אודיו דחוס, ייתכן שה- Audio HAL יצטרך לנתח מטא נתונים בתוך נתוני האודיו הדחוסים כדי לקבוע את משך ההשמעה שלהם.

השהה את התמיכה

אנדרואיד 5 ומטה לא כולל תמיכה בהשהייה. אתה יכול להשהות את השמעת המנהור רק על ידי הרעבה של A/V, אבל אם המאגר הפנימי לווידאו גדול (לדוגמה, יש שנייה אחת של נתונים ברכיב OMX), זה גורם להשהייה להיראות לא מגיבה.

באנדרואיד 5.1 ומעלה, AudioFlinger תומך בהשהיה וחידוש עבור יציאות אודיו ישירות (במנהור). אם ה-HAL מיישם השהייה וחידוש, השהייה והמשך של רצועות מועברים ל-HAL.

רצף השיחה, ההשהיה, ההדחה, חידוש השיחות מכובד על ידי ביצוע קריאות ה-HAL בשרשור ההשמעה (זהה כמו ההורדה).

הצעות יישום

אודיו HAL

עבור אנדרואיד 11, מזהה הסנכרון HW מ-PCR או STC יכול לשמש לסנכרון A/V, כך שזרם וידאו בלבד נתמך.

עבור אנדרואיד 10 ומטה, למכשירים התומכים בהפעלת וידאו במנהור צריך להיות לפחות פרופיל זרם פלט אודיו אחד עם הדגלים FLAG_HW_AV_SYNC ו- AUDIO_OUTPUT_FLAG_DIRECT בקובץ audio_policy.conf שלו. דגלים אלו משמשים להגדרת שעון המערכת משעון האודיו.

OMX

ליצרני התקנים צריכים להיות רכיב OMX נפרד להפעלת וידאו במנהור (יצרנים יכולים לקבל רכיבי OMX נוספים לסוגים אחרים של השמעת אודיו ווידאו, כגון השמעה מאובטחת). הרכיב בעל המנהור צריך:

  • ציין 0 מאגרים ( nBufferCountMin , nBufferCountActual ) ביציאת הפלט שלו.

  • יישם את הרחבה OMX.google.android.index.prepareForAdaptivePlayback setParameter .

  • ציין את היכולות שלו בקובץ media_codecs.xml והכריז על תכונת ההשמעה במנהור. זה גם צריך להבהיר מגבלות על גודל מסגרת, יישור או קצב סיביות. דוגמה מוצגת להלן:

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

אם אותו רכיב OMX משמש לתמיכה בפענוח במנהור ובפענוח ללא מנהור, עליו להשאיר את תכונת ההשמעה במנהור כלא חובה. למפענחים עם מנהור וגם למפענחים ללא מנהור יש את אותן מגבלות יכולת. דוגמה מוצגת להלן:

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

מלחין חומרה (HWC)

כאשר יש שכבה עם מנהור (שכבה עם HWC_SIDEBAND compositionType ) בתצוגה, ה- sidebandStream של השכבה הוא ידית פס הצד המוקצה על ידי רכיב הווידאו OMX.

ה-HWC מסנכרן מסגרות וידאו מפוענחות (מרכיב OMX המנהור) לרצועת האודיו המשויכת (עם מזהה audio-hw-sync ). כאשר מסגרת וידאו חדשה הופכת לעדכנית, ה-HWC מרכיב אותה עם התוכן הנוכחי של כל השכבות שהתקבלו במהלך השיחה האחרונה בהכנה או שהוגדרה, ומציג את התמונה המתקבלת. קריאות ההכנה או ההגדרה מתרחשות רק כאשר שכבות אחרות משתנות, או כאשר המאפיינים של שכבת פס הצד (כגון מיקום או גודל) משתנים.

האיור הבא מייצג את HWC שעובד עם סנכרון החומרה (או הגרעין או מנהל ההתקן), כדי לשלב מסגרות וידאו (7b) עם הקומפוזיציה העדכנית ביותר (7a) לתצוגה בזמן הנכון, בהתבסס על אודיו (7c).

HWC המשלב מסגרות וידאו המבוססות על אודיו

איור 2. סינכרון חומרת HWC (או ליבה או מנהל התקן).