การสร้างอุโมงค์สำหรับมัลติมีเดีย

การทำ Tunnel มัลติมีเดียช่วยให้ข้อมูลวิดีโอที่บีบอัดสามารถส่งผ่านตัวถอดรหัสวิดีโอฮาร์ดแวร์ไปยังจอแสดงผลได้โดยตรงโดยไม่ต้องประมวลผลโดยโค้ดแอปหรือโค้ดเฟรมเวิร์ก Android โค้ดเฉพาะอุปกรณ์ที่อยู่ด้านล่างสแต็ก Android จะกำหนดเฟรมวิดีโอที่จะส่งไปยังจอแสดงผลและเวลาที่ส่งโดยเปรียบเทียบการประทับเวลาของการแสดงเฟรมวิดีโอกับนาฬิกาภายในประเภทใดประเภทหนึ่งต่อไปนี้

  • สําหรับการเล่นวิดีโอแบบออนดีมานด์ใน Android 5 ขึ้นไป จะมีAudioTrack นาฬิกาที่ซิงค์กับการประทับเวลาของงานนำเสนอเสียงที่แอปส่ง

  • สําหรับการเล่นการออกอากาศสดใน Android 11 ขึ้นไป นาฬิกาอ้างอิงโปรแกรม (PCR) หรือนาฬิกาเวลาของระบบ (STC) ที่ขับเคลื่อนโดยจูนเนอร์

ฉากหลัง

การเล่นวิดีโอแบบดั้งเดิมใน Android จะแจ้งเตือนแอปเมื่อมีการถอดรหัสเฟรมวิดีโอที่บีบอัดแล้ว จากนั้นแอปจะปล่อยเฟรมวิดีโอที่ถอดรหัสไปยังจอแสดงผลเพื่อแสดงผลตามเวลาของนาฬิกาของระบบเดียวกันกับเฟรมเสียงที่เกี่ยวข้อง โดยดึงข้อมูลอินสแตนซ์AudioTimestampsที่ผ่านมาเพื่อคำนวณเวลาที่ถูกต้อง

เนื่องจากการเล่นวิดีโอที่ส่งผ่านอุโมงค์จะข้ามโค้ดแอปและลดจำนวนกระบวนการที่ทำงานกับวิดีโอ จึงแสดงผลวิดีโอได้อย่างมีประสิทธิภาพมากขึ้น ทั้งนี้ขึ้นอยู่กับการใช้งาน OEM นอกจากนี้ยังช่วยให้วิดีโอมีจังหวะและการซิงค์กับนาฬิกาที่เลือก (PRC, STC หรือเสียง) ได้อย่างแม่นยำยิ่งขึ้นด้วย โดยหลีกเลี่ยงปัญหาด้านเวลาซึ่งอาจเกิดขึ้นจากการเบี่ยงเบนระหว่างเวลาของคําขอของ Android ในการเรนเดอร์วิดีโอกับเวลาของ vsync ฮาร์ดแวร์จริง อย่างไรก็ตาม การส่งผ่านข้อมูลนี้ยังอาจลดการรองรับเอฟเฟกต์ GPU เช่น การเบลอหรือมุมมนในหน้าต่างภาพซ้อนภาพ (PiP) เนื่องจากบัฟเฟอร์จะข้ามสแต็กกราฟิก Android

แผนภาพต่อไปนี้แสดงวิธีที่การทําอุโมงค์ทําให้กระบวนการเล่นวิดีโอง่ายขึ้น

การเปรียบเทียบโหมดแบบดั้งเดิมกับโหมดอุโมงค์

รูปที่ 1 การเปรียบเทียบกระบวนการเล่นวิดีโอแบบดั้งเดิมและแบบ Tunnel

สำหรับนักพัฒนาแอป

เนื่องจากนักพัฒนาแอปส่วนใหญ่ผสานรวมกับไลบรารีสําหรับการใช้งานการเล่น ในกรณีส่วนใหญ่ การใช้งานจึงจําเป็นต้องกำหนดค่าไลบรารีดังกล่าวใหม่สําหรับการเล่นแบบใช้อุโมงค์เท่านั้น สำหรับการใช้งานระดับล่างของวิดีโอเพลเยอร์ที่ใช้อุโมงค์ ให้ทำตามวิธีการต่อไปนี้

สำหรับการเล่นวิดีโอแบบออนดีมานด์ใน Android 5 ขึ้นไป ให้ทำดังนี้

  1. สร้างอินสแตนซ์ SurfaceView

  2. สร้างอินสแตนซ์ audioSessionId

  3. สร้างอินสแตนซ์ AudioTrack และ MediaCodec ด้วยอินสแตนซ์ audioSessionId ที่สร้างขึ้นในขั้นตอนที่ 2

  4. จัดคิวข้อมูลเสียงไปยัง AudioTrack พร้อมการประทับเวลาของข้อมูลนำเสนอสำหรับเฟรมเสียงแรกในข้อมูลเสียง

สําหรับการเล่นสตรีมแบบสดใน Android 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) หมายความว่าเฟรมวิดีโอแรกที่แสดงควรเป็นเฟรม I เสมอ

    • หากไม่ต้องการให้ระบบแสดงผลเฟรมวิดีโอแรกที่อยู่ในคิวจนกว่าเสียงจะเริ่มเล่น ให้ตั้งค่าพารามิเตอร์นี้เป็น 0

    • หากไม่ได้ตั้งค่าพารามิเตอร์นี้ OEM จะเป็นผู้กำหนดลักษณะการทำงานสำหรับอุปกรณ์

  • เมื่อไม่มีการส่งข้อมูลเสียงไปยัง AudioTrack และบัฟเฟอร์ว่างเปล่า (เสียงเล่นไม่ทัน) การเล่นวิดีโอจะหยุดชั่วคราวจนกว่าจะมีการเขียนข้อมูลเสียงเพิ่มเนื่องจากนาฬิกาเสียงไม่เดินหน้าอีกต่อไป

  • ในระหว่างการเล่น การหยุดชะงักที่แอปแก้ไขไม่ได้อาจปรากฏในการประทับเวลาของการแสดงเสียง เมื่อเกิดกรณีนี้ขึ้น OEM จะแก้ไขช่องว่างเชิงลบด้วยการหยุดเฟรมวิดีโอปัจจุบันไว้ชั่วคราว และแก้ไขช่องว่างเชิงบวกด้วยการทิ้งเฟรมวิดีโอหรือแทรกเฟรมเสียงที่ไม่มีเสียง (ขึ้นอยู่กับการใช้งานของ OEM) ตำแหน่งเฟรม AudioTimestamp จะไม่เพิ่มขึ้นสำหรับเฟรมเสียงที่ไม่มีเสียงที่แทรก

สำหรับผู้ผลิตอุปกรณ์

การกำหนดค่า

OEM ควรสร้างโปรแกรมถอดรหัสวิดีโอแยกต่างหากเพื่อรองรับการเล่นวิดีโอที่ส่งผ่านอุโมงค์ ตัวถอดรหัสนี้ควรโฆษณาว่าสามารถเล่นแบบ Tunnel ในไฟล์ 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 ไปยังคอมโพเนนต์ OMX หรือ Codec2 ไม่ว่ารหัสจะสอดคล้องกับสตรีมเอาต์พุตเสียงหรือการกำหนดค่า Tuner เพื่อให้โค้ด OEM เชื่อมโยงตัวแปลงรหัสกับสตรีมเอาต์พุตเสียงหรือสตรีมจูนเนอร์ที่เกี่ยวข้องได้

ในระหว่างการกําหนดค่าคอมโพเนนต์ คอมโพเนนต์ OMX หรือ Codec2 ควรแสดงผลแฮนเดิลย่านความถี่ที่สามารถใช้เชื่อมโยงตัวแปลงรหัสกับเลเยอร์คอมโพเซอร์ฮาร์ดแวร์ (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 มีหน้าที่รับผิดชอบในการรับบัฟเฟอร์รูปภาพใหม่จากเอาต์พุตของโค้ดที่เวลาที่เหมาะสม โดยอาจซิงค์กับสตรีมเอาต์พุตเสียงที่เกี่ยวข้องหรือนาฬิกาอ้างอิงโปรแกรมของจูนเนอร์ ประกอบบัฟเฟอร์กับเนื้อหาปัจจุบันของเลเยอร์อื่นๆ และแสดงผลภาพที่ได้ ซึ่งจะเกิดขึ้นโดยไม่เกี่ยวข้องกับรอบการเตรียมและการตั้งค่าปกติ การเรียกใช้เตรียมและตั้งค่าจะเกิดขึ้นก็ต่อเมื่อเลเยอร์อื่นๆ มีการเปลี่ยนแปลง หรือเมื่อพร็อพเพอร์ตี้ของเลเยอร์แถบด้านข้าง (เช่น ตำแหน่งหรือขนาด) มีการเปลี่ยนแปลง

OMX

คอมโพเนนต์ตัวถอดรหัสแบบ Tunneled ควรรองรับรายการต่อไปนี้

  • การตั้งค่าพารามิเตอร์แบบขยาย OMX.google.android.index.configureVideoTunnelMode ซึ่งใช้โครงสร้าง ConfigureVideoTunnelModeParams เพื่อส่งรหัส HW_AV_SYNC ที่เชื่อมโยงกับอุปกรณ์เอาต์พุตเสียง

  • การกำหนดค่าพารามิเตอร์ OMX_IndexConfigAndroidTunnelPeek ที่บอกให้ตัวแปลงรหัสแสดงผลหรือไม่แสดงผลเฟรมวิดีโอที่ถอดรหัสเฟรมแรก ไม่ว่าจะมีการเริ่มเล่นเสียงแล้วหรือไม่ก็ตาม

  • ส่งเหตุการณ์ 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;

หากคอมโพเนนต์รองรับการกำหนดค่านี้ ก็ควรจัดสรรตัวแฮนเดิลแบนด์ข้างให้กับตัวแปลงรหัสนี้และส่งกลับผ่านสมาชิก pSidebandWindow เพื่อให้ HWC ระบุตัวแปลงรหัสที่เกี่ยวข้องได้ หากคอมโพเนนต์ไม่รองรับการกำหนดค่านี้ ก็ควรตั้งค่า bTunneled เป็น OMX_FALSE

Codec2

ใน Android 11 ขึ้นไป Codec2 รองรับการเล่นแบบใช้อุโมงค์ คอมโพเนนต์โปรแกรมถอดรหัสควรรองรับสิ่งต่อไปนี้

  • การกำหนดค่า C2PortTunneledModeTuning ซึ่งกำหนดค่าโหมดอุโมงค์และส่งผ่านใน HW_AV_SYNC ที่ดึงมาจากอุปกรณ์เอาต์พุตเสียงหรือการกำหนดค่าจูนเนอร์

  • การค้นหา C2_PARAMKEY_OUTPUT_TUNNEL_HANDLE เพื่อจัดสรรและเรียกใช้ตัวแฮนเดิลแถบข้างสำหรับ HWC

  • การจัดการ C2_PARAMKEY_TUNNEL_HOLD_RENDER เมื่อแนบอยู่กับ C2Work ซึ่งจะสั่งให้ตัวแปลงรหัสถอดรหัสและส่งสัญญาณการทำงานเสร็จสมบูรณ์ แต่จะไม่แสดงผลบัฟเฟอร์เอาต์พุตจนกว่า 1) ระบบจะสั่งให้ตัวแปลงรหัสแสดงผลในภายหลัง หรือ 2) การเล่นเสียงจะเริ่มขึ้น

  • การจัดการ C2_PARAMKEY_TUNNEL_START_RENDER ซึ่งสั่งให้ตัวแปลงรหัสแสดงผลเฟรมที่ทำเครื่องหมายด้วย 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;
}

หากคอมโพเนนต์รองรับการกำหนดค่านี้ ก็ควรจัดสรรตัวแฮนเดิลแบนด์ข้างให้กับตัวแปลงรหัสนี้และส่งกลับผ่าน C2PortTunnelHandlingTuning เพื่อให้ HWC ระบุตัวแปลงรหัสที่เกี่ยวข้องได้

HAL เสียง

สําหรับการเล่นวิดีโอแบบออนดีมานด์ HAL เสียงจะรับการนําเสนอเสียงที่มีการประทับเวลาในบรรทัดเดียวกับข้อมูลเสียงในรูปแบบ Big-endian ภายในส่วนหัวที่พบตอนต้นของแต่ละบล็อกข้อมูลเสียงที่แอปเขียน

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 แสดงผลเฟรมวิดีโอให้สอดคล้องกับเฟรมเสียงที่เกี่ยวข้อง HAL ของเสียงควรแยกวิเคราะห์ส่วนหัวการซิงค์และใช้การประทับเวลาของการแสดงเพื่อซิงค์นาฬิกาการเล่นกับการแสดงผลเสียงอีกครั้ง หากต้องการซิงค์อีกครั้งเมื่อเล่นเสียงที่บีบอัดแล้ว HAL เสียงอาจต้องแยกวิเคราะห์ข้อมูลเมตาภายในข้อมูลเสียงที่บีบอัดเพื่อระบุระยะเวลาการเล่น

หยุดการสนับสนุนชั่วคราว

Android 5 หรือต่ำกว่าไม่รองรับการหยุดชั่วคราว คุณจะหยุดเล่นวิดีโอที่ส่งผ่านอุโมงค์ชั่วคราวได้ก็ต่อเมื่อไม่มีพื้นที่เก็บข้อมูล A/V เพียงพอเท่านั้น แต่หากบัฟเฟอร์ภายในสำหรับวิดีโอมีขนาดใหญ่ (เช่น มีข้อมูล 1 วินาทีในคอมโพเนนต์ OMX) ระบบจะทําให้ดูเหมือนว่าหยุดชั่วคราวไม่ตอบสนอง

ใน Android 5.1 ขึ้นไป AudioFlinger จะรองรับการหยุดชั่วคราวและเล่นต่อสำหรับเอาต์พุตเสียงโดยตรง (ที่มีการส่งผ่านข้อมูล) หาก HAL ใช้การหยุดชั่วคราวและเล่นต่อ ระบบจะส่งต่อการหยุดชั่วคราวและการกลับมาเล่นต่อของแทร็กไปยัง HAL

ระบบจะดำเนินการตามลำดับการเรียกใช้การหยุดชั่วคราว ล้างข้อมูล และเล่นต่อโดยเรียกใช้ HAL ในเธรดการเล่น (เหมือนกับการโอน)

คำแนะนำในการใช้งาน

HAL เสียง

สำหรับ Android 11 จะใช้รหัสการซิงค์ HW จาก PCR หรือ STC สำหรับการซิงค์ A/V ได้ ดังนั้นจึงรองรับสตรีมวิดีโอเท่านั้น

สำหรับ Android 10 หรือต่ำกว่า อุปกรณ์ที่รองรับการเล่นวิดีโอแบบ Tunneled ควรมีโปรไฟล์สตรีมเอาต์พุตเสียงอย่างน้อย 1 รายการที่มี Flag FLAG_HW_AV_SYNC และ AUDIO_OUTPUT_FLAG_DIRECT ในไฟล์ audio_policy.conf ค่าสถานะเหล่านี้ใช้ตั้งค่านาฬิกาของระบบจากนาฬิกาเสียง

OMX

ผู้ผลิตอุปกรณ์ควรมีคอมโพเนนต์ OMX แยกต่างหากสำหรับการเล่นวิดีโอแบบ Tunnel (ผู้ผลิตอาจมีคอมโพเนนต์ 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 เดียวกันเพื่อรองรับการถอดรหัสแบบ Tunneled และแบบไม่ใช้ Tunneled คุณควรตั้งค่าฟีเจอร์การเล่นแบบ Tunneled ไว้ว่าไม่จำเป็น ทั้งโปรแกรมถอดรหัสแบบใช้อุโมงค์และแบบไม่ใช้อุโมงค์จึงมีข้อจำกัดด้านความสามารถเหมือนกัน ตัวอย่างแสดงอยู่ด้านล่าง

<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 ที่ทำงานร่วมกับตัวซิงค์ฮาร์ดแวร์ (หรือเคอร์เนลหรือไดรเวอร์) เพื่อรวมเฟรมวิดีโอ (7ข) เข้ากับการคอมโพสล่าสุด (7ก) เพื่อแสดงในเวลาที่เหมาะสม โดยอิงตามเสียง (7ค)

HWC รวมเฟรมวิดีโอตามเสียง

รูปที่ 2 ตัวซิงค์ฮาร์ดแวร์ (หรือเคอร์เนลหรือไดรเวอร์) HWC